PackageInstall
阅读packageInstall源码我们的突破口是从程序的窗口开始,首先查看AndroidManifest文件中activity的定义
PackageInstallerActivity:
窗口指定了两个IntentFilter,因此两种方式调用该窗口,两种方式都定义了action: android.intent.action.INSTALL_PACKAGE,第一种方式需要在apk文件路径前加"file:"而且扩展名必须为apk,这个现实由android:mimeType决定,而第二种方式除了加"file:"前缀,还可以加"package:"前缀,对扩展名没有限制
UninstallerActivity:
只有一个IntentFilter,并且指定了两个action,在卸载应用程序时,指定哪一个action都可以,除此之外,还需要卸载程序的package,并且package必须加"package:"前缀
InstallAppProgress和UninstallerActivity没有定义任何action,因此无法被其他应用调用
这个四个activity都没有指定 android.intent.action.MAIN所以在程序列表中是没有图标的,PackageInstallerActivity和InstallAppProgress用于安装应用程序,UninstallerActivity和UninstallerActivity用于卸载应用程序(如果activity定义了action为MAIN,category为LAUNCHER,就会出现多个应用程序图标启动对应activity)
安装apk: 通过指定apk文件的绝对路径安装
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(new File("/ascard/qq.apk")),"application/vnd.android.package-archive"); //满足第一个IntentFilter
// intent.setData(Uri.fromFile(new File("/sdcard/qq.apk"))); //满足第二个IntentFilter(没有指定MimeType)
startActivity(intent);
安装apk: 如果通过package方式安装,前提是该应用已经安装,也就是这种事方式是升级应用,如果未安装该应用,会弹出错误提示框
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(Uri.parse("package:com.tencent.mobileqq"));
startActivity(intent);
卸载apk: 卸载apk只有一种方式,需要指定卸载应用的package,不过有两个可以设置的action
Intent intent=new Intent(Intent.ACTION_DELETE);
// Intent intent=new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.parse("package:com.tencent.mobileqq"));
startActivity(intent);
应用程序安装源码:
<span style="color:#666666">package com.android.packageinstaller;
/*
* This activity is launched when a new application is installed via side loading
* The package is first parsed and the user is notified of parse errors via a dialog.
* If the package is successfully parsed, the user is notified to turn on the install unknown
* applications setting. A memory check is made at this point and the user is notified of out
* of memory conditions if any. If the package is already existing on the device,
* a confirmation dialog (to replace the existing package) is presented to the user.
* Based on the user response the package is then installed by launching InstallAppConfirm
* sub activity. All state transitions are handled in this activity
*/
public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
private static final String TAG = "PackageInstaller";
private int mSessionId = -1;
private Uri mPackageURI;
private Uri mOriginatingURI;
private Uri mReferrerURI;
private int mOriginatingUid = VerificationParams.NO_UID;
private ManifestDigest mPkgDigest;
private boolean localLOGV = false;
PackageManager mPm;
PackageInstaller mInstaller;
PackageInfo mPkgInfo;
ApplicationInfo mSourceInfo;
// ApplicationInfo object primarily used for already existing applications
private ApplicationInfo mAppInfo = null;
private InstallFlowAnalytics mInstallFlowAnalytics;
// View for install progress
View mInstallConfirm;
// Buttons to indicate user acceptance
private Button mOk;
private Button mCancel;
CaffeinatedScrollView mScrollView = null;
private boolean mOkCanInstall = false;
static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String TAB_ID_ALL = "all";
private static final String TAB_ID_NEW = "new";
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
private static final int DLG_UNKNOWN_APPS = DLG_BASE + 1;
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
private static final int DLG_ALLOW_SOURCE = DLG_BASE + 5;
private void startInstallConfirm() {
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
if (TAB_ID_ALL.equals(tabId)) {
mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
} else if (TAB_ID_NEW.equals(tabId)) {
mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
}
}
});
boolean permVisible = false; // true: 显示权限列表 false: 未显示权限列表
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
if (mPkgInfo != null) {
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
// 获取隐私相关权限的数量
final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
// 获取与设备相关权限的数量
final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
if (mAppInfo != null) {
// 判断是否为系统应用,并使用不同的布局文件
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this); //用来显示权限列表的滚动View
// 如果显示的内容超过mScrollView,就会折叠可以滚动
mScrollView.setFillViewport(true);
boolean newPermissionsFound =
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
// 针对更新应用程序相对旧版本而已判断是否加入了新的权限
if (newPermissionsFound) {
permVisible = true;
// 将新的权限列表视频添加到滚动视图中
mScrollView.addView(perms.getPermissionsView(
AppSecurityPermissions.WHICH_NEW));
} else { // 没有设置任何权限,只显示应用程序名称和图标
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
getText(R.string.newPerms)), mScrollView);
} else { // 无法获取应用的任何信息,将相应的控件隐藏
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.divider).setVisibility(View.VISIBLE);
}
// 如果至少设置了一个权限
if (NP > 0 || ND > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View root = inflater.inflate(R.layout.permissions_list, null);
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
}
// 有与隐私相关的权限
if (NP > 0) {
// 在滚动视图上显示与隐私相关的权限
((ViewGroup)root.findViewById(R.id.privacylist)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
} else {
root.findViewById(R.id.privacylist).setVisibility(View.GONE);
}
// 有与设备相关的权限
if (ND > 0) {
// 在滚动视图上显示与设备相关的权限
((ViewGroup)root.findViewById(R.id.devicelist)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
} else {
root.findViewById(R.id.devicelist).setVisibility(View.GONE);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
getText(R.string.allPerms)), root);
}
}
mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
// 没有任何权限
if (!permVisible) {
if (mAppInfo != null) {
// This is an update to an application, but there are no
// permissions at all.
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system_no_perms
: R.string.install_confirm_question_update_no_perms;
} else {
// This is a new application with no permissions.
msg = R.string.install_confirm_question_no_perms;
}
tabHost.setVisibility(View.GONE);
mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
findViewById(R.id.filler).setVisibility(View.VISIBLE);
findViewById(R.id.divider).setVisibility(View.GONE);
mScrollView = null;
}
if (msg != 0) {
((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
}
// mInstallConfirm为显示权限列表的最顶层视图
mInstallConfirm.setVisibility(View.VISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
// 应用程序没有任何权限,直接将按钮设为"安装"
mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
// 显示权限列表是,当滚动到最后是,将按钮设置为"安装",否则是"下一步"
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
mOk.setText(R.string.install);
mOkCanInstall = true;
}
});
}
}
private void showDialogInner(int id) {
// TODO better fix for this? Remove dialog so that it gets created again
removeDialog(id);
showDialog(id);
}
@Override
public Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case DLG_UNKNOWN_APPS:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
.setMessage(R.string.unknown_apps_dlg_text)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Finishing off activity so that user can navigate to settings manually");
finish();
}})
.setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Launching settings");
launchSettingsAppAndFinish();
}
})
.setOnCancelListener(this)
.create();
case DLG_PACKAGE_ERROR :
return new AlertDialog.Builder(this)
.setTitle(R.string.Parse_error_dlg_title)
.setMessage(R.string.Parse_error_dlg_text)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_OUT_OF_SPACE:
// Guaranteed not to be null. will default to package name if not set by app
CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText = getString(R.string.out_of_space_dlg_text,
appTitle.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.out_of_space_dlg_title)
.setMessage(dlgText)
.setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//launch manage applications
Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Canceling installation");
finish();
}
})
.setOnCancelListener(this)
.create();
case DLG_INSTALL_ERROR :
// Guaranteed not to be null. will default to package name if not set by app
CharSequence appTitle1 = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
String dlgText1 = getString(R.string.install_failed_msg,
appTitle1.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.install_failed)
.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setMessage(dlgText1)
.setOnCancelListener(this)
.create();
case DLG_ALLOW_SOURCE:
CharSequence appTitle2 = mPm.getApplicationLabel(mSourceInfo);
String dlgText2 = getString(R.string.allow_source_dlg_text,
appTitle2.toString());
return new AlertDialog.Builder(this)
.setTitle(R.string.allow_source_dlg_title)
.setMessage(dlgText2)
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_CANCELED);
finish();
}})
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = getSharedPreferences(PREFS_ALLOWED_SOURCES,
Context.MODE_PRIVATE);
prefs.edit().putBoolean(mSourceInfo.packageName, true).apply();
startInstallConfirm();
}
})
.setOnCancelListener(this)
.create();
}
return null;
}
private void launchSettingsAppAndFinish() {
// Create an intent to launch SettingsTwo activity
Intent launchSettingsIntent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
launchSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launchSettingsIntent);
finish();
}
private boolean isInstallingUnknownAppsAllowed() {
UserManager um = (UserManager) getSystemService(USER_SERVICE);
boolean disallowedByUserManager = um.getUserRestrictions()
.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, false);
// 获取contentProvider中"未知来源"是否选中来判断
// 执行这句代码没有添加任何权限,但是前提是对apk进行系统签名,也就是说必须和源码一起编译或者使用mm/mmm命令单独进行编译
// 普通安装应用尽管编译能通过,但是执行到这里的时候回报错
boolean allowedBySecureSettings = Settings.Secure.getInt(getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
return (allowedBySecureSettings && (!disallowedByUserManager));
}
private boolean isInstallRequestFromUnknownSource(Intent intent) {
String callerPackage = getCallingPackage();
if (callerPackage != null && intent.getBooleanExtra(
Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
try {
mSourceInfo = mPm.getApplicationInfo(callerPackage, 0);
if (mSourceInfo != null) {
if ((mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
// Privileged apps are not considered an unknown source.
return false;
}
}
} catch (NameNotFoundException e) {
}
}
return true;
}
private boolean isVerifyAppsEnabled() {
UserManager um = (UserManager) getSystemService(USER_SERVICE);
if (um.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS)) {
return true;
}
return Settings.Global.getInt(getContentResolver(),
Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) > 0;
}
private boolean isAppVerifierInstalled() {
final PackageManager pm = getPackageManager();
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.setType(PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0);
return (receivers.size() > 0) ? true : false;
}
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
startInstallConfirm();// 显示一个确认对话框,安装的核心代码
}
void setPmResult(int pmResult) {
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult);
setResult(pmResult == PackageManager.INSTALL_SUCCEEDED
? RESULT_OK : RESULT_FIRST_USER, result);
}
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
final Intent intent = getIntent();
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
mInstallFlowAnalytics = new InstallFlowAnalytics();
mInstallFlowAnalytics.setContext(this);
mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(
isInstallingUnknownAppsAllowed());
mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
finish();
return;
}
final PackageUtil.AppSnippet as;
// 处理scheme为package的情况
if ("package".equals(mPackageURI.getScheme())) {
mInstallFlowAnalytics.setFileUri(false);
try {
// 获取package对应的Android应用信息,如应用名称,权限列表..
mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
// 如果无法获取,则弹出一个错误提示框,然后退出安装
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + mPackageURI.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);
return;
}
// 创建AppSnippet对象,该对象封装了用于待安装Android应用的标题和图标
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} else { // 处理scheme为 file 的情况
mInstallFlowAnalytics.setFileUri(true);
// 获取apk文件的实际路径
final File sourceFile = new File(mPackageURI.getPath());
// 创建apk文件的分析器
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
// 如果无法创建文件分析器,则直接退出,弹出对话框
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
mInstallFlowAnalytics.setPackageInfoObtained();
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);
return;
}
// 获取apk文件的相关信息
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mPkgDigest = parsed.manifestDigest;
// 另一种创建APPSnippet的方式,使用更好的方式提取应用程序名称和图标
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
mInstallFlowAnalytics.setPackageInfoObtained();
// 设置activity的视图
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
// 创建显示应用名称(TextView)和图标(ImageView)的控件
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
// 获取应用的ID 对于普通应用,该值没有什么作用
mOriginatingUid = getOriginatingUid(intent);
// 判断系统是否允许安装未知来源的应用
// Block the install attempt on the Unknown Sources setting if necessary.
if ((requestFromUnknownSource) && (!isInstallingUnknownAppsAllowed())) {
//ask user to enable setting first
// 询问是否通过设置打开"未知来源"
showDialogInner(DLG_UNKNOWN_APPS);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
return;
}
// 初始化安装
initiateInstall();
}
/** Get the ApplicationInfo for the calling package, if available */
private ApplicationInfo getSourceInfo() {
String callingPackage = getCallingPackage();
if (callingPackage != null) {
try {
return mPm.getApplicationInfo(callingPackage, 0);
} catch (NameNotFoundException ex) {
// ignore
}
}
return null;
}
/** Get the originating uid if possible, or VerificationParams.NO_UID if not available */
private int getOriginatingUid(Intent intent) {
// The originating uid from the intent. We only trust/use this if it comes from a
// system application
int uidFromIntent = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
VerificationParams.NO_UID);
// Get the source info from the calling package, if available. This will be the
// definitive calling package, but it only works if the intent was started using
// startActivityForResult,
ApplicationInfo sourceInfo = getSourceInfo();
if (sourceInfo != null) {
if (uidFromIntent != VerificationParams.NO_UID &&
(mSourceInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
return uidFromIntent;
}
// We either didn't get a uid in the intent, or we don't trust it. Use the
// uid of the calling package instead.
return sourceInfo.uid;
}
// We couldn't get the specific calling package. Let's get the uid instead
int callingUid;
try {
callingUid = ActivityManagerNative.getDefault()
.getLaunchedFromUid(getActivityToken());
} catch (android.os.RemoteException ex) {
Log.w(TAG, "Could not determine the launching uid.");
// nothing else we can do
return VerificationParams.NO_UID;
}
// If we got a uid from the intent, we need to verify that the caller is a
// privileged system package before we use it
if (uidFromIntent != VerificationParams.NO_UID) {
String[] callingPackages = mPm.getPackagesForUid(callingUid);
if (callingPackages != null) {
for (String packageName: callingPackages) {
try {
ApplicationInfo applicationInfo =
mPm.getApplicationInfo(packageName, 0);
if ((applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
return uidFromIntent;
}
} catch (NameNotFoundException ex) {
// ignore it, and try the next package
}
}
}
}
// We either didn't get a uid from the intent, or we don't trust it. Use the
// calling uid instead.
return callingUid;
}
@Override
public void onBackPressed() {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
super.onBackPressed();
}
// Generic handling when pressing back key
public void onCancel(DialogInterface dialog) {
finish();
}
public void onClick(View v) {
if(v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
// We're only confirming permissions, so we don't really know how the
// story ends; assume success.
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
} else {
// Start subactivity to actually install the application
// 执行安装事件时,首先会给Intent传递很多值给InstallAppProgress,对于普通的应用程序很多只都是null,
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
// 设置要安装的URI
newIntent.setData(mPackageURI);
// 指定安装activity
newIntent.setClass(this, InstallAppProgress.class);
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
newIntent.putExtra(
InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != VerificationParams.NO_UID) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
}
finish();
} else { // 点击"下一步"继续显示权限列表
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if(v == mCancel) { // 点击"取消"退出安装
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
}
}
}
</span>
startInstallConfirm方法主要是通过getPermissionsView方法获取不同权限的View,并在滚动视图中显示这些view
AppSecurityPermissions.WHICH_NEW 新加入的权限
AppSecurityPermissions.WHICH_PERSONAL 与用户隐私相关的权限
AppSecurityPermissions.WHICH_DEVICE 与设备相关的权限
<span style="color:#666666">package com.android.packageinstaller;
/**
* This activity corresponds to a download progress screen that is displayed
* when the user tries
* to install an application bundled as an apk file. The result of the application install
* is indicated in the result code that gets set to the corresponding installation status
* codes defined in PackageManager. If the package being installed already exists,
* the existing package is replaced with the new one.
*/
public class InstallAppProgress extends Activity implements View.OnClickListener, OnCancelListener {
private final String TAG="InstallAppProgress";
private boolean localLOGV = false;
static final String EXTRA_MANIFEST_DIGEST =
"com.android.packageinstaller.extras.manifest_digest";
static final String EXTRA_INSTALL_FLOW_ANALYTICS =
"com.android.packageinstaller.extras.install_flow_analytics";
private ApplicationInfo mAppInfo;
private Uri mPackageURI;
private InstallFlowAnalytics mInstallFlowAnalytics;
private ProgressBar mProgressBar;
private View mOkPanel;
private TextView mStatusTextView;
private TextView mExplanationTextView;
private Button mDoneButton;
private Button mLaunchButton;
private final int INSTALL_COMPLETE = 1;
private Intent mLaunchIntent;
private static final int DLG_OUT_OF_SPACE = 1;
private CharSequence mLabel;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case INSTALL_COMPLETE: //成功安装
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(msg.arg1);
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT, msg.arg1);
setResult(msg.arg1 == PackageManager.INSTALL_SUCCEEDED
? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
result);
finish();
return;
}
// Update the status text
mProgressBar.setVisibility(View.INVISIBLE);
// Show the ok button
int centerTextLabel;
int centerExplanationLabel = -1;
LevelListDrawable centerTextDrawable = (LevelListDrawable) getResources()
.getDrawable(R.drawable.ic_result_status);
if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {
mLaunchButton.setVisibility(View.VISIBLE);
centerTextDrawable.setLevel(0);
centerTextLabel = R.string.install_done;
// Enable or disable launch button
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
mAppInfo.packageName);
boolean enabled = false;
if(mLaunchIntent != null) {
List<ResolveInfo> list = getPackageManager().
queryIntentActivities(mLaunchIntent, 0);
if (list != null && list.size() > 0) {
enabled = true;
}
}
if (enabled) {
// 该按钮用于打开已经安装成功的程序
mLaunchButton.setOnClickListener(InstallAppProgress.this);
} else {
mLaunchButton.setEnabled(false);
}
// 由于空间不足导致安装失败
} else if (msg.arg1 == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE){
showDialogInner(DLG_OUT_OF_SPACE);
return;
} else { // 由于其他原因导致安装失败
// Generic error handling for all other error codes.
centerTextDrawable.setLevel(1);
centerExplanationLabel = getExplanationFromErrorCode(msg.arg1);
centerTextLabel = R.string.install_failed;
mLaunchButton.setVisibility(View.INVISIBLE);
}
if (centerTextDrawable != null) {
centerTextDrawable.setBounds(0, 0,
centerTextDrawable.getIntrinsicWidth(),
centerTextDrawable.getIntrinsicHeight());
mStatusTextView.setCompoundDrawablesRelative(centerTextDrawable, null,
null, null);
}
mStatusTextView.setText(centerTextLabel);
if (centerExplanationLabel != -1) {
mExplanationTextView.setText(centerExplanationLabel);
mExplanationTextView.setVisibility(View.VISIBLE);
} else {
mExplanationTextView.setVisibility(View.GONE);
}
mDoneButton.setOnClickListener(InstallAppProgress.this);
mOkPanel.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
};
private int getExplanationFromErrorCode(int errCode) {
Log.d(TAG, "Installation error code: " + errCode);
switch (errCode) {
case PackageManager.INSTALL_FAILED_INVALID_APK:
return R.string.install_failed_invalid_apk;
case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:
return R.string.install_failed_inconsistent_certificates;
case PackageManager.INSTALL_FAILED_OLDER_SDK:
return R.string.install_failed_older_sdk;
case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:
return R.string.install_failed_cpu_abi_incompatible;
default:
return -1;
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
// 获取待安装应用相关的值mAppInfo和mPackageURI
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
mInstallFlowAnalytics.setContext(this);
mPackageURI = intent.getData();
// 验证scheme是否为file或package
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
// 安装应用的核心代码
initView();
}
@Override
public Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case DLG_OUT_OF_SPACE:
String dlgText = getString(R.string.out_of_space_dlg_text, mLabel);
return new AlertDialog.Builder(this)
.setTitle(R.string.out_of_space_dlg_title)
.setMessage(dlgText)
.setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//launch manage applications
Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
startActivity(intent);
finish();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "Canceling installation");
finish();
}
})
.setOnCancelListener(this)
.create();
}
return null;
}
private void showDialogInner(int id) {
removeDialog(id);
showDialog(id);
}
class PackageInstallObserver extends IPackageInstallObserver.Stub {
public void packageInstalled(String packageName, int returnCode) {
// 该方法会在其他线程中调用,因此通过handler更新UI
Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
msg.arg1 = returnCode;
mHandler.sendMessage(msg);
}
}
public void initView() {
setContentView(R.layout.op_progress);
int installFlags = 0;
PackageManager pm = getPackageManager();
try {
// 如果待安装的程序已经安装,返回PackageInfo对象,否则返回null
PackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if(pi != null) {
// 如果应用已经安装,设置安装模式为更新
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
} catch (NameNotFoundException e) {
}
if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
final PackageUtil.AppSnippet as;
// 如果scheme为package,意味着更新程序
if ("package".equals(mPackageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
pm.getApplicationIcon(mAppInfo));
} else { // 通过apk文件路径获取显示应用相关信息的视图
final File sourceFile = new File(mPackageURI.getPath());
as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
}
mLabel = as.label;
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mStatusTextView = (TextView)findViewById(R.id.center_text);
mStatusTextView.setText(R.string.installing);
mExplanationTextView = (TextView) findViewById(R.id.center_explanation);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setIndeterminate(true);
// Hide button till progress is being displayed
mOkPanel = (View)findViewById(R.id.buttons_panel);
mDoneButton = (Button)findViewById(R.id.done_button);
mLaunchButton = (Button)findViewById(R.id.launch_button);
mOkPanel.setVisibility(View.INVISIBLE);
// 获取待安装应用的package name
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
// 对于普通的Android应用没下吗连个uri为null
Uri originatingURI = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
Uri referrer = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
int originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
VerificationParams.NO_UID);
ManifestDigest manifestDigest = getIntent().getParcelableExtra(EXTRA_MANIFEST_DIGEST);
VerificationParams verificationParams = new VerificationParams(null, originatingURI,
referrer, originatingUid, manifestDigest);
// 安装成功,失败的监听
PackageInstallObserver observer = new PackageInstallObserver();
// 核心逻辑
if ("package".equals(mPackageURI.getScheme())) {
try {
//根据package name更新应用
pm.installExistingPackage(mAppInfo.packageName);
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_SUCCEEDED);
} catch (PackageManager.NameNotFoundException e) {
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_FAILED_INVALID_APK);
}
} else {
// 安装或更新应用
pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
installerPackageName, verificationParams, null);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public void onClick(View v) {
if(v == mDoneButton) {
if (mAppInfo.packageName != null) {
Log.i(TAG, "Finished installing "+mAppInfo.packageName);
}
finish();
} else if(v == mLaunchButton) {
startActivity(mLaunchIntent);
finish();
}
}
public void onCancel(DialogInterface dialog) {
finish();
}
}
</span>
PackageManager的installExistingPackage和installPackageWithVerificationAndEncryption都是静默安装,安装过程不会出现任何提示,不过这两个方法被设为hide,不能在普通Android应用中调用
查看源码发现安装过程中弹出的校验窗口是PackageInstaller有意展示的,同时Android的静默安装时通过如上方法实现的
应用程序卸载源码:
<span style="color:#666666">package com.android.packageinstaller; public class UninstallerActivity extends Activity { private static final String TAG = "UninstallerActivity"; public static class UninstallAlertDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final PackageManager pm = getActivity().getPackageManager(); final DialogInfo dialogInfo = ((UninstallerActivity) getActivity()).mDialogInfo; final CharSequence appLabel = dialogInfo.appInfo.loadLabel(pm); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); StringBuilder messageBuilder = new StringBuilder(); // If the Activity label differs from the App label, then make sure the user // knows the Activity belongs to the App being uninstalled. if (dialogInfo.activityInfo != null) { final CharSequence activityLabel = dialogInfo.activityInfo.loadLabel(pm); if (!activityLabel.equals(appLabel)) { messageBuilder.append( getString(R.string.uninstall_activity_text, activityLabel)); messageBuilder.append(" ").append(appLabel).append(".\n\n"); } } final boolean isUpdate = ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); if (isUpdate) { messageBuilder.append(getString(R.string.uninstall_update_text)); } else { UserManager userManager = UserManager.get(getActivity()); if (dialogInfo.allUsers && userManager.getUserCount() >= 2) { messageBuilder.append(getString(R.string.uninstall_application_text_all_users)); } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) { UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier()); messageBuilder.append( getString(R.string.uninstall_application_text_user, userInfo.name)); } else { messageBuilder.append(getString(R.string.uninstall_application_text)); } } dialogBuilder.setTitle(appLabel); dialogBuilder.setIcon(dialogInfo.appInfo.loadIcon(pm)); dialogBuilder.setPositiveButton(android.R.string.ok, this); dialogBuilder.setNegativeButton(android.R.string.cancel, this); dialogBuilder.setMessage(messageBuilder.toString()); return dialogBuilder.create(); } @Override public void onClick(DialogInterface dialog, int which) { if (which == Dialog.BUTTON_POSITIVE) { // 开始卸载应用 ((UninstallerActivity) getActivity()).startUninstallProgress(); } else { // 取消 ((UninstallerActivity) getActivity()).dispatchAborted(); } } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); getActivity().finish(); } } public static class AppNotFoundDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new AlertDialog.Builder(getActivity()) .setTitle(R.string.app_not_found_dlg_title) .setMessage(R.string.app_not_found_dlg_text) .setNeutralButton(android.R.string.ok, null) .create(); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); ((UninstallerActivity) getActivity()).dispatchAborted(); getActivity().setResult(Activity.RESULT_FIRST_USER); getActivity().finish(); } } static class DialogInfo { ApplicationInfo appInfo; ActivityInfo activityInfo; boolean allUsers; UserHandle user; IBinder callback; } private DialogInfo mDialogInfo; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // Get intent information. // We expect an intent with URI of the form package://<packageName>#<className> // className is optional; if specified, it is the activity the user chose to uninstall final Intent intent = getIntent(); final Uri packageUri = intent.getData(); if (packageUri == null) { Log.e(TAG, "No package URI in intent"); showAppNotFound(); return; } final String packageName = packageUri.getEncodedSchemeSpecificPart(); if (packageName == null) { Log.e(TAG, "Invalid package name in URI: " + packageUri); showAppNotFound(); return; } final IPackageManager pm = IPackageManager.Stub.asInterface( ServiceManager.getService("package")); mDialogInfo = new DialogInfo(); mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER); if (mDialogInfo.user == null) { mDialogInfo.user = android.os.Process.myUserHandle(); } mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false); mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK); try { mDialogInfo.appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Unable to get packageName. Package manager is dead?"); } if (mDialogInfo.appInfo == null) { Log.e(TAG, "Invalid packageName: " + packageName); showAppNotFound(); return; } // The class name may have been specified (e.g. when deleting an app from all apps) final String className = packageUri.getFragment(); if (className != null) { try { mDialogInfo.activityInfo = pm.getActivityInfo( new ComponentName(packageName, className), 0, mDialogInfo.user.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Unable to get className. Package manager is dead?"); // Continue as the ActivityInfo isn't critical. } } showConfirmationDialog(); } private void showConfirmationDialog() { showDialogFragment(new UninstallAlertDialogFragment()); } private void showAppNotFound() { showDialogFragment(new AppNotFoundDialogFragment()); } private void showDialogFragment(DialogFragment fragment) { FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag("dialog"); if (prev != null) { ft.remove(prev); } fragment.show(ft, "dialog"); } void startUninstallProgress() { Intent newIntent = new Intent(Intent.ACTION_VIEW); // newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user); // 要求卸载该应用对于用户程序和数据 newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers); newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback); // appInfo封装了卸载程序的信息(ApplicationInfo对象) newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo); // 允许卸载窗口返回是否卸载成功的标识 if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } // 指定卸载应用的activity newIntent.setClass(this, UninstallAppProgress.class); startActivity(newIntent); } void dispatchAborted() { if (mDialogInfo != null && mDialogInfo.callback != null) { final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface( mDialogInfo.callback); try { observer.onPackageDeleted(mDialogInfo.appInfo.packageName, PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"); } catch (RemoteException ignored) { } } } }</span>
<span style="color:#666666">package com.android.packageinstaller;
public class UninstallAppProgress extends Activity implements OnClickListener {
private final String TAG="UninstallAppProgress";
private boolean localLOGV = false;
private ApplicationInfo mAppInfo;
private boolean mAllUsers;
private UserHandle mUser;
private IBinder mCallback;
private TextView mStatusTextView;
private Button mOkButton;
private Button mDeviceManagerButton;
private ProgressBar mProgressBar;
private View mOkPanel;
private volatile int mResultCode = -1;
private static final int UNINSTALL_COMPLETE = 1;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UNINSTALL_COMPLETE: //完成了卸载
mResultCode = msg.arg1;
final String packageName = (String) msg.obj;
if (mCallback != null) {
final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
.asInterface(mCallback);
try {
observer.onPackageDeleted(mAppInfo.packageName, mResultCode,
packageName);
} catch (RemoteException ignored) {
}
finish();
return;
}
// 返回卸载的状态
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);
// 根据是否成功卸载返回对应的值
setResult(mResultCode == PackageManager.DELETE_SUCCEEDED
? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
result);
finish();
return;
}
// 下面是处理不需要返回卸载状态的情况
// Update the status text
final String statusText;
switch (msg.arg1) {
// 成功卸载
case PackageManager.DELETE_SUCCEEDED:
statusText = getString(R.string.uninstall_done);
// Show a Toast and finish the activity
Context ctx = getBaseContext();
Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
setResultAndFinish(mResultCode);
return;
// 由于安全策略的原因导致卸载失败
case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER:
Log.d(TAG, "Uninstall failed because " + packageName
+ " is a device admin");
mDeviceManagerButton.setVisibility(View.VISIBLE);
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 = getString(R.string.uninstall_failed);
break;
}
mStatusTextView.setText(statusText);
// Hide the progress bar; Show the ok button
mProgressBar.setVisibility(View.INVISIBLE);
mOkPanel.setVisibility(View.VISIBLE);
break;
default:
break;
}
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
// 获取ApplicationInfo对象
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);
}
}
mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
initView(); // 卸载的逻辑
}
// 卸载监听类
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
public void packageDeleted(String packageName, int returnCode) {
Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
msg.arg1 = returnCode;
msg.obj = packageName;
mHandler.sendMessage(msg);
}
}
void setResultAndFinish(int retCode) {
setResult(retCode);
finish();
}
public void initView() {
// 获取待卸载程序 是否为被更新的系统应用(该标识只作为标题显示用)
boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title);
setContentView(R.layout.uninstall_progress);
// Initialize views
View snippetView = findViewById(R.id.app_snippet);
PackageUtil.initSnippetForInstalledApp(this, mAppInfo, snippetView);
mStatusTextView = (TextView) findViewById(R.id.center_text);
mStatusTextView.setText(R.string.uninstalling);
mDeviceManagerButton = (Button) findViewById(R.id.device_manager_button);
mDeviceManagerButton.setVisibility(View.GONE);
// 当由于某些安全策略的原因卸载失败,会显示该按钮,点击进入设置activity来改变某些安全策略
mDeviceManagerButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.Settings$DeviceAdminSettingsActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
});
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setIndeterminate(true);
// Hide button till progress is being displayed
mOkPanel = (View) findViewById(R.id.ok_panel);
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();
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) {
if(v == mOkButton) {
Log.i(TAG, "Finished uninstalling pkg: " + mAppInfo.packageName);
setResultAndFinish(mResultCode);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent ev) {
if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (mResultCode == -1) {
// Ignore back key when installation is in progress
return true;
} else {
// If installation is done, just set the result code
setResult(mResultCode);
}
}
return super.dispatchKeyEvent(ev);
}
}</span>
如果要实现应用程序静默安装和卸载都需要在AndroidManifest文件中添加以下权限(该权限属于系统级别的权限,不能在普通的Android应用中使用)
<users-permission android:name="android.permission.INSTALL_PACKAGES">
必须对Android应用进行系统签名(platform签名),并且通过刷机或root权限安装到Android系统中