summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Guy <kennyguy@google.com>2014-06-03 17:50:38 +0100
committerKenny Guy <kennyguy@google.com>2014-07-10 16:37:55 +0100
commit596ce64f71011e7600ee2be66d977dafb86b9da3 (patch)
treea6f63132b25528b6b0631dabdddc1ec36ee0615c
parent365b378c1770a5bc75da34c2bb26cc48f4c49c5c (diff)
downloadandroid_packages_apps_PackageInstaller-596ce64f71011e7600ee2be66d977dafb86b9da3.tar.gz
android_packages_apps_PackageInstaller-596ce64f71011e7600ee2be66d977dafb86b9da3.tar.bz2
android_packages_apps_PackageInstaller-596ce64f71011e7600ee2be66d977dafb86b9da3.zip
Support uninstalling apps for other profiles.
Allow client to pick which user the package will be removed for, checking its a profile of the current user. Inform user if package is blocked from being removed and let them knows its due to an admin. Add check to stop non owner asking to uninstall for all users. Bug: 14127299 Change-Id: I60504224f5271272a390320a0fa62aa7f5de4e54
-rw-r--r--AndroidManifest.xml1
-rw-r--r--res/values/strings.xml9
-rw-r--r--src/com/android/packageinstaller/PackageUtil.java27
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallAppProgress.java77
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallerActivity.java42
5 files changed, 134 insertions, 22 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8b04c7fa..93ed65d2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<application android:label="@string/app_name"
android:allowBackup="false"
android:theme="@android:style/Theme.DeviceDefault.DialogWhenLarge.NoActionBar"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a43af793..23211cdd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -113,6 +113,7 @@
<string name="uninstall_application_text">Do you want to uninstall this app?</string>
<string name="uninstall_application_text_all_users">Do you want to uninstall this app for <b>all</b>
users? The application and its data will be removed from <b>all</b> users on the device.</string>
+ <string name="uninstall_application_text_user">Do you want to uninstall this app for the user <xliff:g id="username">%1$s</xliff:g>?</string>
<string name="uninstall_update_text">Do you want to replace this app with the factory version?</string>
<string name="uninstalling">Uninstalling\u2026</string>
<string name="uninstall_done">Uninstall finished.</string>
@@ -121,6 +122,14 @@
is a current device administrator [CHAR LIMIT=80] -->
<string name="uninstall_failed_device_policy_manager">Can\'t uninstall because this package is an
active device administrator.</string>
+ <!-- String presented to the user when uninstalling a package failed because a profile owner
+ has marked the the target package as not able to be uninstalled [CHAR LIMIT=80] -->
+ <string name="uninstall_blocked_profile_owner">This app is needed for
+ your <xliff:g id="username">%1$s</xliff:g> profile and can\'t be uninstalled.</string>
+ <!-- String presented to the user when uninstalling a package failed because a device owner
+ has marked the the target package as not able to be uninstalled [CHAR LIMIT=80] -->
+ <string name="uninstall_blocked_device_owner">This app is required
+ by your device administrator and can\'t be uninstalled.</string>
<!-- String on a button that leads to the "device administrator" configuration setting where a
user will be able to disable the device administrator in order to uninstall
it. [CHAR LIMIT=50] -->
diff --git a/src/com/android/packageinstaller/PackageUtil.java b/src/com/android/packageinstaller/PackageUtil.java
index 63121e4a..bb445824 100644
--- a/src/com/android/packageinstaller/PackageUtil.java
+++ b/src/com/android/packageinstaller/PackageUtil.java
@@ -18,6 +18,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -30,6 +31,8 @@ import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import android.os.UserHandle;
+import android.os.UserManager;
import java.io.File;
import java.util.List;
@@ -91,11 +94,33 @@ public class PackageUtil {
*/
public static View initSnippetForInstalledApp(Activity pContext,
ApplicationInfo appInfo, View snippetView) {
+ return initSnippetForInstalledApp(pContext, appInfo, snippetView, null);
+ }
+
+ /**
+ * Utility method to display a snippet of an installed application.
+ * The content view should have been set on context before invoking this method.
+ * appSnippet view should include R.id.app_icon and R.id.app_name
+ * defined on it.
+ *
+ * @param pContext context of package that can load the resources
+ * @param componentInfo ComponentInfo object whose resources are to be loaded
+ * @param snippetView the snippet view
+ * @param UserHandle user that the app si installed for.
+ */
+ public static View initSnippetForInstalledApp(Activity pContext,
+ ApplicationInfo appInfo, View snippetView, UserHandle user) {
final PackageManager pm = pContext.getPackageManager();
+ Drawable icon = appInfo.loadIcon(pm);
+ if (user != null) {
+ final UserManager userManager = (UserManager) pContext.getSystemService(
+ Context.USER_SERVICE);
+ icon = userManager.getBadgedDrawableForUser(icon, user);
+ }
return initSnippet(
snippetView,
appInfo.loadLabel(pm),
- appInfo.loadIcon(pm));
+ icon);
}
/**
diff --git a/src/com/android/packageinstaller/UninstallAppProgress.java b/src/com/android/packageinstaller/UninstallAppProgress.java
index 7aa0a2e0..397236a6 100755
--- a/src/com/android/packageinstaller/UninstallAppProgress.java
+++ b/src/com/android/packageinstaller/UninstallAppProgress.java
@@ -21,10 +21,16 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@@ -34,6 +40,8 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
+import java.util.List;
+
/**
* This activity corresponds to a download progress screen that is displayed
* when an application is uninstalled. The result of the application uninstall
@@ -46,6 +54,7 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
private boolean localLOGV = false;
private ApplicationInfo mAppInfo;
private boolean mAllUsers;
+ private UserHandle mUser;
private TextView mStatusTextView;
private Button mOkButton;
private Button mDeviceManagerButton;
@@ -73,10 +82,10 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
final String packageName = (String) msg.obj;
// Update the status text
- final int statusText;
+ final String statusText;
switch (msg.arg1) {
case PackageManager.DELETE_SUCCEEDED:
- statusText = R.string.uninstall_done;
+ statusText = getString(R.string.uninstall_done);
// Show a Toast and finish the activity
Context ctx = getBaseContext();
Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
@@ -86,12 +95,46 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
Log.d(TAG, "Uninstall failed because " + packageName
+ " is a device admin");
mDeviceManagerButton.setVisibility(View.VISIBLE);
- statusText = R.string.uninstall_failed_device_policy_manager;
+ statusText = getString(R.string.uninstall_failed_device_policy_manager);
+ break;
+ case PackageManager.DELETE_FAILED_OWNER_BLOCKED:
+ UserManager userManager =
+ (UserManager) getSystemService(Context.USER_SERVICE);
+ IPackageManager packageManager = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ List<UserInfo> users = userManager.getUsers();
+ int blockingUserId = UserHandle.USER_NULL;
+ for (int i = 0; i < users.size(); ++i) {
+ final UserInfo user = users.get(i);
+ try {
+ if (packageManager.getBlockUninstallForUser(packageName,
+ user.id)) {
+ blockingUserId = user.id;
+ break;
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Log.e(TAG, "Failed to talk to package manager", e);
+ }
+ }
+ mDeviceManagerButton.setVisibility(View.VISIBLE);
+ if (blockingUserId == UserHandle.USER_OWNER) {
+ statusText = getString(R.string.uninstall_blocked_device_owner);
+ } else if (blockingUserId == UserHandle.USER_NULL) {
+ Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+ + msg.arg1 + " no blocking user");
+ statusText = getString(R.string.uninstall_failed);
+ } else {
+ String userName = userManager.getUserInfo(blockingUserId).name;
+ statusText = String.format(
+ getString(R.string.uninstall_blocked_profile_owner),
+ userName);
+ }
break;
default:
Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+ msg.arg1);
- statusText = R.string.uninstall_failed;
+ statusText = getString(R.string.uninstall_failed);
break;
}
mStatusTextView.setText(statusText);
@@ -112,6 +155,20 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+ if (mAllUsers && UserHandle.myUserId() != UserHandle.USER_OWNER) {
+ throw new SecurityException("Only owner user can request uninstall for all users");
+ }
+ mUser = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (mUser == null) {
+ mUser = android.os.Process.myUserHandle();
+ } else {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ List<UserHandle> profiles = userManager.getUserProfiles();
+ if (!profiles.contains(mUser)) {
+ throw new SecurityException("User " + android.os.Process.myUserHandle() + " can't "
+ + "request uninstall for user " + mUser);
+ }
+ }
initView();
}
@@ -159,9 +216,17 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
mOkButton = (Button) findViewById(R.id.ok_button);
mOkButton.setOnClickListener(this);
mOkPanel.setVisibility(View.INVISIBLE);
+ IPackageManager packageManager =
+ IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
PackageDeleteObserver observer = new PackageDeleteObserver();
- getPackageManager().deletePackage(mAppInfo.packageName, observer,
- mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);
+ try {
+ packageManager.deletePackageAsUser(mAppInfo.packageName, observer,
+ mUser.getIdentifier(),
+ mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Log.e(TAG, "Failed to talk to package manager", e);
+ }
}
public void onClick(View v) {
diff --git a/src/com/android/packageinstaller/UninstallerActivity.java b/src/com/android/packageinstaller/UninstallerActivity.java
index 1b9bdce9..b64ce6f4 100755
--- a/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/src/com/android/packageinstaller/UninstallerActivity.java
@@ -25,24 +25,20 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
-import java.util.List;
-
/*
* This activity presents UI to uninstall an application. Usually launched with intent
* Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
@@ -53,10 +49,12 @@ public class UninstallerActivity extends Activity implements OnClickListener,
private static final String TAG = "UninstallerActivity";
private boolean localLOGV = false;
PackageManager mPm;
+ private IPackageManager mIpm;
private ApplicationInfo mAppInfo;
private boolean mAllUsers;
private Button mOk;
private Button mCancel;
+ private UserHandle mUserHandle;
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
@@ -104,6 +102,7 @@ public class UninstallerActivity extends Activity implements OnClickListener,
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mAppInfo);
newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mAllUsers);
+ newIntent.putExtra(Intent.EXTRA_USER, mUserHandle);
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -128,11 +127,19 @@ public class UninstallerActivity extends Activity implements OnClickListener,
return;
}
+ mUserHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (mUserHandle == null) {
+ mUserHandle = android.os.Process.myUserHandle();
+ }
+
mPm = getPackageManager();
+ mIpm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+
boolean errFlag = false;
try {
- mAppInfo = mPm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
+ mAppInfo = mIpm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES,
+ mUserHandle.getIdentifier());
+ } catch (RemoteException e) {
errFlag = true;
}
@@ -143,8 +150,9 @@ public class UninstallerActivity extends Activity implements OnClickListener,
ActivityInfo activityInfo = null;
if (className != null) {
try {
- activityInfo = mPm.getActivityInfo(new ComponentName(packageName, className), 0);
- } catch (NameNotFoundException e) {
+ activityInfo = mIpm.getActivityInfo(new ComponentName(packageName, className), 0,
+ mUserHandle.getIdentifier());
+ } catch (RemoteException e) {
errFlag = true;
}
}
@@ -162,10 +170,14 @@ public class UninstallerActivity extends Activity implements OnClickListener,
setTitle(R.string.uninstall_update_title);
confirm.setText(R.string.uninstall_update_text);
} else {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
setTitle(R.string.uninstall_application_title);
- if (mAllUsers && ((UserManager)getSystemService(
- Context.USER_SERVICE)).getUsers().size() >= 2) {
+ if (mAllUsers && userManager.getUsers().size() >= 2) {
confirm.setText(R.string.uninstall_application_text_all_users);
+ } else if (!mUserHandle.equals(android.os.Process.myUserHandle())) {
+ String userName = userManager.getUserInfo(mUserHandle.getIdentifier()).name;
+ confirm.setText(String.format(
+ getString(R.string.uninstall_application_text_user), userName));
} else {
confirm.setText(R.string.uninstall_application_text);
}
@@ -184,7 +196,7 @@ public class UninstallerActivity extends Activity implements OnClickListener,
}
View snippetView = findViewById(R.id.uninstall_activity_snippet);
- PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);
+ PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView, mUserHandle);
//initialize ui elements
mOk = (Button)findViewById(R.id.ok_button);