summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);