OSDN Git Service

Spooler should not crash if print service config activities are not exported.
authorSvetoslav Ganov <svetoslavganov@google.com>
Sat, 14 Sep 2013 07:59:03 +0000 (00:59 -0700)
committerSvetoslav Ganov <svetoslavganov@google.com>
Sat, 14 Sep 2013 08:00:55 +0000 (01:00 -0700)
1. If a print service does not export its activities for settings and
   adding printers the print spooler ignores them instead of crashing.
   Also if the service is not enabled its activities are now ignored.

2. Added a dedicated permission for a print service to optionally
   protect its settings and add printer activities such that only the
   system can bind to them.

3. Fixed a crash in the print dialog if its content is detached
   from the window and animators are running.

bug:10680224

Change-Id: I20b57d6622a15f9b2352ba78d04c44e67b316a15

core/java/android/print/IPrintManager.aidl
core/java/android/print/PrintManager.java
core/java/android/printservice/PrintService.java
packages/PrintSpooler/AndroidManifest.xml
packages/PrintSpooler/res/values/strings.xml
packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
services/java/com/android/server/print/PrintManagerService.java
services/java/com/android/server/print/UserState.java

index fb6bb2e..d2ae5e6 100644 (file)
@@ -22,6 +22,7 @@ import android.print.IPrintClient;
 import android.print.PrinterId;
 import android.print.PrintJobInfo;
 import android.print.PrintAttributes;
+import android.printservice.PrintServiceInfo;
 
 /**
  * Interface for communication with the core print manager service.
@@ -37,6 +38,8 @@ interface IPrintManager {
     void cancelPrintJob(int printJobId, int appId, int userId);
     void restartPrintJob(int printJobId, int appId, int userId);
 
+    List<PrintServiceInfo> getEnabledPrintServices(int userId);
+
     void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
     void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
             in List<PrinterId> priorityList, int userId);
index 6e32c05..10cc771 100644 (file)
@@ -28,6 +28,7 @@ import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.print.PrintDocumentAdapter.LayoutResultCallback;
 import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.printservice.PrintServiceInfo;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -204,6 +205,25 @@ public final class PrintManager {
     }
 
     /**
+     * Gets the list of enabled print services.
+     *
+     * @return The enabled service list or an empty list.
+     *
+     * @hide
+     */
+    public List<PrintServiceInfo> getEnabledPrintServices() {
+        try {
+            List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
+            if (enabledServices != null) {
+                return enabledServices;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error getting the enalbed print services", re);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * @hide
      */
     public PrinterDiscoverySession createPrinterDiscoverySession() {
index 012e76a..e5ebf77 100644 (file)
@@ -178,6 +178,14 @@ public abstract class PrintService extends Service {
      * For detailed configuration options that can be specified via the meta-data
      * refer to {@link android.R.styleable#PrintService android.R.styleable.PrintService}.
      * </p>
+     * <p>
+     * If you declare a settings or add a printers activity, they have to be exported,
+     * by setting the {@link android.R.attr#exported} activity attribute to <code>true
+     * </code>. Also in case you want only the system to be able to start any of these
+     * activities you can specify that they request the android.permission
+     * .START_PRINT_SERVICE_CONFIG_ACTIVITY permission by setting the
+     * {@link android.R.attr#permission} activity attribute.
+     * </p>
      */
     public static final String SERVICE_META_DATA = "android.printservice";
 
index 83ec1ad..9319025 100644 (file)
         android:versionCode="1">
 
     <!-- Allows an application to call APIs that give it access to all print jobs
-         on the device. Usually an app can access only the print jobs it created.
-    -->
+         on the device. Usually an app can access only the print jobs it created. -->
     <permission
         android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"
         android:label="@string/permlab_accessAllPrintJobs"
         android:description="@string/permdesc_accessAllPrintJobs"
         android:protectionLevel="signature" />
 
+    <!-- May be required by the settings and add printer activities of a
+         print service if the developer wants only trusted system code to
+         be able to launch these activities. -->
+    <permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+        android:label="@string/permlab_startPrintServiceConfigActivity"
+        android:description="@string/permdesc_startPrintServiceConfigActivity"
+        android:protectionLevel="signature" />
+
     <uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
-    <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
 
     <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
 
index 235a7a1..21a4867 100644 (file)
     <string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs
         created by another app. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want
+         to allow the application to do this. -->
+    <string name="permlab_startPrintServiceConfigActivity">start print service configuration activities</string>
+    <!-- Description of an application permission, listed so the user can choose whether they
+         want to allow the application to do this. -->
+    <string name="permdesc_startPrintServiceConfigActivity">Allows the holder to start the
+        configuration activities of a print service. Should never be needed for normal apps.</string>
+
 </resources>
index 5c3d700..14f60f1 100644 (file)
@@ -62,9 +62,11 @@ import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
@@ -1371,7 +1373,8 @@ public class PrintJobConfigActivity extends Activity {
                     null, false);
 
             // First animation - fade out the old content.
-            hidingView.animate().alpha(0.0f).withLayer().withEndAction(new Runnable() {
+            AutoCancellingAnimator.animate(hidingView).alpha(0.0f)
+                    .withLayer().withEndAction(new Runnable() {
                 @Override
                 public void run() {
                     hidingView.setVisibility(View.INVISIBLE);
@@ -1390,8 +1393,8 @@ public class PrintJobConfigActivity extends Activity {
                             / (float) contentContainer.getHeight();
 
                     // Second animation - resize the container.
-                    contentContainer.animate().scaleY(scaleY).withLayer().withEndAction(
-                            new Runnable() {
+                    AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY).withLayer()
+                            .withEndAction(new Runnable() {
                         @Override
                         public void run() {
                             // Swap the old and the new content.
@@ -1400,8 +1403,8 @@ public class PrintJobConfigActivity extends Activity {
                             contentContainer.addView(showingView);
 
                             // Third animation - show the new content.
-                            showingView.animate().withLayer().alpha(1.0f).withEndAction(
-                                    new Runnable() {
+                            AutoCancellingAnimator.animate(showingView).withLayer().alpha(1.0f)
+                                    .withEndAction(new Runnable() {
                                 @Override
                                 public void run() {
                                     postAnimateCommand.run();
@@ -2212,4 +2215,67 @@ public class PrintJobConfigActivity extends Activity {
             }
         }
     }
+
+    private static final class AutoCancellingAnimator
+            implements OnAttachStateChangeListener, Runnable {
+
+        private ViewPropertyAnimator mAnimator;
+
+        private boolean mCancelled;
+        private Runnable mEndCallback;
+
+        public static AutoCancellingAnimator animate(View view) {
+            ViewPropertyAnimator animator = view.animate();
+            AutoCancellingAnimator cancellingWrapper =
+                    new AutoCancellingAnimator(animator);
+            view.addOnAttachStateChangeListener(cancellingWrapper);
+            return cancellingWrapper;
+        }
+
+        private AutoCancellingAnimator(ViewPropertyAnimator animator) {
+            mAnimator = animator;
+        }
+
+        public AutoCancellingAnimator alpha(float alpha) {
+            mAnimator = mAnimator.alpha(alpha);
+            return this;
+        }
+
+        public void cancel() {
+            mAnimator.cancel();
+        }
+
+        public AutoCancellingAnimator withLayer() {
+            mAnimator = mAnimator.withLayer();
+            return this;
+        }
+
+        public AutoCancellingAnimator withEndAction(Runnable callback) {
+            mEndCallback = callback;
+            mAnimator = mAnimator.withEndAction(this);
+            return this;
+        }
+
+        public AutoCancellingAnimator scaleY(float scale) {
+            mAnimator = mAnimator.scaleY(scale);
+            return this;
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            cancel();
+        }
+
+        @Override
+        public void run() {
+            if (!mCancelled) {
+                mEndCallback.run();
+            }
+        }
+    }
 }
index 9ca3a86..c397c40 100644 (file)
@@ -24,20 +24,26 @@ import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.app.ListFragment;
 import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.Loader;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.print.PrintManager;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -59,6 +65,8 @@ import java.util.List;
  */
 public final class SelectPrinterFragment extends ListFragment {
 
+    private static final String LOG_TAG = "SelectPrinterFragment";
+
     private static final int LOADER_ID_PRINTERS_LOADER = 1;
 
     private static final String FRAGMRNT_TAG_ADD_PRINTER_DIALOG =
@@ -142,40 +150,45 @@ public final class SelectPrinterFragment extends ListFragment {
     private void updateAddPrintersAdapter() {
         mAddPrinterServices.clear();
 
-        // Get all print services.
-        List<ResolveInfo> resolveInfos = getActivity().getPackageManager().queryIntentServices(
-                new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        // Get all enabled print services.
+        PrintManager printManager = (PrintManager) getActivity()
+                .getSystemService(Context.PRINT_SERVICE);
+        List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
 
-        // No print services - done.
-        if (resolveInfos.isEmpty()) {
+        // No enabled print services - done.
+        if (enabledServices.isEmpty()) {
             return;
         }
 
         // Find the services with valid add printers activities.
-        final int resolveInfoCount = resolveInfos.size();
-        for (int i = 0; i < resolveInfoCount; i++) {
-            ResolveInfo resolveInfo = resolveInfos.get(i);
-
-            PrintServiceInfo printServiceInfo = PrintServiceInfo.create(
-                    resolveInfo, getActivity());
-            String addPrintersActivity = printServiceInfo.getAddPrintersActivityName();
+        final int enabledServiceCount = enabledServices.size();
+        for (int i = 0; i < enabledServiceCount; i++) {
+            PrintServiceInfo enabledService = enabledServices.get(i);
 
             // No add printers activity declared - done.
-            if (TextUtils.isEmpty(addPrintersActivity)) {
+            if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
                 continue;
             }
 
+            ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
             ComponentName addPrintersComponentName = new ComponentName(
-                    resolveInfo.serviceInfo.packageName,
-                    addPrintersActivity);
-            Intent addPritnersIntent = new Intent(Intent.ACTION_MAIN)
+                    serviceInfo.packageName, enabledService.getAddPrintersActivityName());
+            Intent addPritnersIntent = new Intent()
                 .setComponent(addPrintersComponentName);
 
             // The add printers activity is valid - add it.
-            if (!getActivity().getPackageManager().queryIntentActivities(
-                    addPritnersIntent, 0).isEmpty()) {
-                mAddPrinterServices.add(printServiceInfo);
+            PackageManager pm = getActivity().getPackageManager();
+            List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
+            if (!resolvedActivities.isEmpty()) {
+                // The activity is a component name, therefore it is one or none.
+                ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
+                if (activityInfo.exported
+                        && (activityInfo.permission == null
+                                || pm.checkPermission(activityInfo.permission,
+                                        getActivity().getPackageName())
+                                        == PackageManager.PERMISSION_GRANTED)) {
+                    mAddPrinterServices.add(enabledService);
+                }
             }
         }
     }
@@ -228,7 +241,11 @@ public final class SelectPrinterFragment extends ListFragment {
                             printService.getAddPrintersActivityName());
                     Intent intent = new Intent(Intent.ACTION_MAIN);
                     intent.setComponent(componentName);
-                    startActivity(intent);
+                    try {
+                        startActivity(intent);
+                    } catch (ActivityNotFoundException anfe) {
+                        Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
+                    }
                 }
             });
 
@@ -238,7 +255,11 @@ public final class SelectPrinterFragment extends ListFragment {
                 builder.setPositiveButton(R.string.search_play_store,
                     new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int whichButton) {
-                            startActivity(marketIntent);
+                            try {
+                                startActivity(marketIntent);
+                            } catch (ActivityNotFoundException anfe) {
+                                Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
+                            }
                         }
                     });
             }
index 926f822..c33bfb7 100644 (file)
@@ -35,6 +35,7 @@ import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintAttributes;
 import android.print.PrintJobInfo;
 import android.print.PrinterId;
+import android.printservice.PrintServiceInfo;
 import android.provider.Settings;
 import android.util.SparseArray;
 
@@ -192,6 +193,22 @@ public final class PrintManagerService extends IPrintManager.Stub {
         }
     }
 
+
+    @Override
+    public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return userState.getEnabledPrintServices();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
             int userId) {
index 86a5aed..6b68c61 100644 (file)
@@ -134,6 +134,26 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
         }
     }
 
+    public List<PrintServiceInfo> getEnabledPrintServices() {
+        synchronized (mLock) {
+            List<PrintServiceInfo> enabledServices = null;
+            final int installedServiceCount = mInstalledServices.size();
+            for (int i = 0; i < installedServiceCount; i++) {
+                PrintServiceInfo installedService = mInstalledServices.get(i);
+                ComponentName componentName = new ComponentName(
+                        installedService.getResolveInfo().serviceInfo.packageName,
+                        installedService.getResolveInfo().serviceInfo.name);
+                if (mActiveServices.containsKey(componentName)) {
+                    if (enabledServices == null) {
+                        enabledServices = new ArrayList<PrintServiceInfo>();
+                    }
+                    enabledServices.add(installedService);
+                }
+            }
+            return enabledServices;
+        }
+    }
+
     public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();