OSDN Git Service

Added an api to uninstall a packge with active DAs
authorSuprabh Shukla <suprabh@google.com>
Fri, 29 Jan 2016 02:05:14 +0000 (18:05 -0800)
committerSuprabh Shukla <suprabh@google.com>
Mon, 8 Feb 2016 21:43:56 +0000 (13:43 -0800)
The api deactivates all the active admins in the package, then force
stops the package and starts the uninstall intent for the package. This
is intended to provide an easy way for a user to delete a misbehaving
Device Admin

Bug: b/22359208
Change-Id: Ic7ddd89ef6db53e7e76f805808d9e806100374db

core/java/android/app/admin/DevicePolicyManager.java
core/java/android/app/admin/IDevicePolicyManager.aidl
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java

index 9bad2f9..53ca180 100644 (file)
@@ -5635,4 +5635,32 @@ public class DevicePolicyManager {
             return false;
         }
     }
+
+    /**
+     * @hide
+     * Returns whether the uninstall for {@code packageName} for the current user is in queue
+     * to be started
+     * @param packageName the package to check for
+     * @return whether the uninstall intent for {@code packageName} is pending
+     */
+    public boolean isUninstallInQueue(String packageName) {
+        try {
+            return mService.isUninstallInQueue(packageName);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+            return false;
+        }
+    }
+
+    /**
+     * @hide
+     * @param packageName the package containing active DAs to be uninstalled
+     */
+    public void uninstallPackageWithActiveAdmins(String packageName) {
+        try {
+            mService.uninstallPackageWithActiveAdmins(packageName);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+        }
+    }
 }
index b57e1b7..c4bbc65 100644 (file)
@@ -291,4 +291,7 @@ interface IDevicePolicyManager {
     boolean getDeviceLoggingEnabled(in ComponentName admin);
     ParceledListSlice retrieveDeviceLogs(in ComponentName admin);
     ParceledListSlice retrievePreviousDeviceLogs(in ComponentName admin);
+
+    boolean isUninstallInQueue(String packageName);
+    void uninstallPackageWithActiveAdmins(String packageName);
 }
index cfe147e..43d1d47 100644 (file)
@@ -115,6 +115,7 @@ import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -273,6 +274,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     private static final int PROFILE_KEYGUARD_FEATURES =
             PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_AFFECT_PROFILE;
 
+    private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -280,6 +283,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     final UserManagerInternal mUserManagerInternal;
     private final LockPatternUtils mLockPatternUtils;
 
+    /**
+     * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
+     * is requested for user u.
+     */
+    private final Set<Pair<String, Integer>> mPackagesToRemove =
+            new ArraySet<Pair<String, Integer>>();
+
     final LocalService mLocalService;
 
     // Stores and loads state on device and profile owners.
@@ -2014,35 +2024,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
         if (admin != null) {
             getUserData(userHandle).mRemovingAdmins.add(adminReceiver);
-
             sendAdminCommandLocked(admin,
                     DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED,
                     new BroadcastReceiver() {
                         @Override
                         public void onReceive(Context context, Intent intent) {
-                            synchronized (DevicePolicyManagerService.this) {
-                                int userHandle = admin.getUserHandle().getIdentifier();
-                                DevicePolicyData policy = getUserData(userHandle);
-                                boolean doProxyCleanup = admin.info.usesPolicy(
-                                        DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
-                                policy.mAdminList.remove(admin);
-                                policy.mAdminMap.remove(adminReceiver);
-                                validatePasswordOwnerLocked(policy);
-                                if (doProxyCleanup) {
-                                    resetGlobalProxyLocked(getUserData(userHandle));
-                                }
-                                saveSettingsLocked(userHandle);
-                                updateMaximumTimeToLockLocked(userHandle);
-                                policy.mRemovingAdmins.remove(adminReceiver);
-                            }
-                            // The removed admin might have disabled camera, so update user
-                            // restrictions.
-                            pushUserRestrictions(userHandle);
+                            removeAdminArtifacts(adminReceiver, userHandle);
+                            removePackageIfRequired(adminReceiver.getPackageName(), userHandle);
                         }
                     });
         }
     }
 
+
     public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
             boolean throwForMissiongPermission) {
         if (!mHasFeature) {
@@ -8395,4 +8389,131 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
         List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
         return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
     }
+
+    private void enforceCanManageDeviceAdmin() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+                null);
+    }
+
+    @Override
+    public boolean isUninstallInQueue(final String packageName) {
+        enforceCanManageDeviceAdmin();
+        final int userId = mInjector.userHandleGetCallingUserId();
+        Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            return mPackagesToRemove.contains(packageUserPair);
+        }
+    }
+
+    @Override
+    public void uninstallPackageWithActiveAdmins(final String packageName) {
+        enforceCanManageDeviceAdmin();
+        Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
+
+        final int userId = mInjector.userHandleGetCallingUserId();
+
+        final ComponentName profileOwner = getProfileOwner(userId);
+        if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
+        }
+
+        final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false);
+        if (getDeviceOwnerUserId() == userId && deviceOwner != null
+                && packageName.equals(deviceOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
+        }
+
+        final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            mPackagesToRemove.add(packageUserPair);
+        }
+
+        final List<ComponentName> activeAdminsList = getActiveAdmins(userId);
+        if (activeAdminsList == null || activeAdminsList.size() == 0) {
+            startUninstallIntent(packageName, userId);
+            return;
+        }
+
+        for (ComponentName activeAdmin : activeAdminsList) {
+            if (packageName.equals(activeAdmin.getPackageName())) {
+                removeActiveAdmin(activeAdmin, userId);
+            }
+        }
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                for (ComponentName activeAdmin : activeAdminsList) {
+                    removeAdminArtifacts(activeAdmin, userId);
+                }
+                startUninstallIntent(packageName, userId);
+            }
+        }, DEVICE_ADMIN_DEACTIVATE_TIMEOUT); // Start uninstall after timeout anyway.
+    }
+
+    private void removePackageIfRequired(final String packageName, final int userId) {
+        if (!packageHasActiveAdmins(packageName, userId)) {
+            // Will not do anything if uninstall was not requested or was already started.
+            startUninstallIntent(packageName, userId);
+        }
+    }
+
+    private void startUninstallIntent(final String packageName, final int userId) {
+        final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
+        synchronized (this) {
+            if (!mPackagesToRemove.contains(packageUserPair)) {
+                // Do nothing if uninstall was not requested or was already started.
+                return;
+            }
+            mPackagesToRemove.remove(packageUserPair);
+        }
+        try {
+            if (mInjector.getIPackageManager().getPackageInfo(packageName, 0, userId) == null) {
+                // Package does not exist. Nothing to do.
+                return;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Failure talking to PackageManager while getting package info");
+        }
+
+        try { // force stop the package before uninstalling
+            mInjector.getIActivityManager().forceStopPackage(packageName, userId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+        }
+        final Uri packageURI = Uri.parse("package:" + packageName);
+        final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
+        uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivityAsUser(uninstallIntent, UserHandle.of(userId));
+    }
+
+    /**
+     * Removes the admin from the policy. Ideally called after the admin's
+     * {@link DeviceAdminReceiver#onDisabled(Context, Intent)} has been successfully completed.
+     *
+     * @param adminReceiver The admin to remove
+     * @param userHandle The user for which this admin has to be removed.
+     */
+    private void removeAdminArtifacts(final ComponentName adminReceiver, final int userHandle) {
+        synchronized (this) {
+            final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+            if (admin == null) {
+                return;
+            }
+            final DevicePolicyData policy = getUserData(userHandle);
+            final boolean doProxyCleanup = admin.info.usesPolicy(
+                    DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+            policy.mAdminList.remove(admin);
+            policy.mAdminMap.remove(adminReceiver);
+            validatePasswordOwnerLocked(policy);
+            if (doProxyCleanup) {
+                resetGlobalProxyLocked(policy);
+            }
+            saveSettingsLocked(userHandle);
+            updateMaximumTimeToLockLocked(userHandle);
+            policy.mRemovingAdmins.remove(adminReceiver);
+        }
+        // The removed admin might have disabled camera, so update user
+        // restrictions.
+        pushUserRestrictions(userHandle);
+    }
 }