summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorSteve Kondik <shade@chemlab.org>2012-11-18 22:51:13 -0800
committerSteve Kondik <shade@chemlab.org>2012-11-18 22:51:13 -0800
commit32f720afab05e43b3a291afd76e75a6b1c2080c4 (patch)
tree3da93935a5961254eaa0a9d00ed661c020e9bbc3 /src/com/android
parent771c0f58c04ff478e0666dd04b07d8abe4c5d2e3 (diff)
parent3e76ae182cdb2d44a59ef3762321018bdb4de8dc (diff)
downloadandroid_packages_apps_PackageInstaller-32f720afab05e43b3a291afd76e75a6b1c2080c4.tar.gz
android_packages_apps_PackageInstaller-32f720afab05e43b3a291afd76e75a6b1c2080c4.tar.bz2
android_packages_apps_PackageInstaller-32f720afab05e43b3a291afd76e75a6b1c2080c4.zip
Merge branch 'jb-mr1-release' of https://android.googlesource.com/platform/packages/apps/PackageInstaller into HEADcm-10.1-M1
Change-Id: Ib1b0bbed7b33842394ef77676885a4d1940b6fdb
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/packageinstaller/CaffeinatedScrollView.java75
-rwxr-xr-xsrc/com/android/packageinstaller/InstallAppProgress.java34
-rw-r--r--src/com/android/packageinstaller/PackageInstallerActivity.java478
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallAppProgress.java7
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallerActivity.java13
5 files changed, 506 insertions, 101 deletions
diff --git a/src/com/android/packageinstaller/CaffeinatedScrollView.java b/src/com/android/packageinstaller/CaffeinatedScrollView.java
new file mode 100644
index 00000000..723ae13c
--- /dev/null
+++ b/src/com/android/packageinstaller/CaffeinatedScrollView.java
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.packageinstaller;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+/**
+ * It's a ScrollView that knows how to stay awake.
+ */
+class CaffeinatedScrollView extends ScrollView {
+ private Runnable mFullScrollAction;
+ private int mBottomSlop;
+
+ public CaffeinatedScrollView(Context context) {
+ super(context);
+ }
+
+ public CaffeinatedScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Make this visible so we can call it
+ */
+ @Override
+ public boolean awakenScrollBars() {
+ return super.awakenScrollBars();
+ }
+
+ public void setFullScrollAction(Runnable action) {
+ mFullScrollAction = action;
+ mBottomSlop = (int)(4 * getResources().getDisplayMetrics().density);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ checkFullScrollAction();
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ checkFullScrollAction();
+ }
+
+ private void checkFullScrollAction() {
+ if (mFullScrollAction != null) {
+ int daBottom = getChildAt(0).getBottom();
+ int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
+ if ((daBottom - screenBottom) < mBottomSlop) {
+ mFullScrollAction.run();
+ mFullScrollAction = null;
+ }
+ }
+ }
+}
diff --git a/src/com/android/packageinstaller/InstallAppProgress.java b/src/com/android/packageinstaller/InstallAppProgress.java
index 8bfcd4f1..fc820782 100755
--- a/src/com/android/packageinstaller/InstallAppProgress.java
+++ b/src/com/android/packageinstaller/InstallAppProgress.java
@@ -28,6 +28,7 @@ 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.VerificationParams;
import android.graphics.drawable.LevelListDrawable;
import android.net.Uri;
import android.os.Bundle;
@@ -162,7 +163,7 @@ public class InstallAppProgress extends Activity implements View.OnClickListener
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
- if (scheme != null && !"file".equals(scheme)) {
+ if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
@@ -226,8 +227,14 @@ public class InstallAppProgress extends Activity implements View.OnClickListener
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
- final File sourceFile = new File(mPackageURI.getPath());
- PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
+ final PackageUtil.AppSnippet as;
+ if ("package".equals(mPackageURI.getScheme())) {
+ as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
+ pm.getApplicationIcon(mAppInfo));
+ } else {
+ 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);
@@ -243,8 +250,27 @@ public class InstallAppProgress extends Activity implements View.OnClickListener
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ 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);
+ VerificationParams verificationParams = new VerificationParams(null, originatingURI,
+ referrer, originatingUid, null);
PackageInstallObserver observer = new PackageInstallObserver();
- pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
+
+ if ("package".equals(mPackageURI.getScheme())) {
+ try {
+ 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
diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java
index 99631035..4a6db210 100644
--- a/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
@@ -25,13 +26,18 @@ import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageUserState;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser;
+import android.content.pm.VerificationParams;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -39,7 +45,6 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AppSecurityPermissions;
import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TabHost;
import android.widget.TabWidget;
@@ -61,9 +66,13 @@ import java.util.ArrayList;
public class PackageInstallerActivity extends Activity implements OnCancelListener, OnClickListener {
private static final String TAG = "PackageInstaller";
private Uri mPackageURI;
+ private Uri mOriginatingURI;
+ private Uri mReferrerURI;
+ private int mOriginatingUid = VerificationParams.NO_UID;
+
private boolean localLOGV = false;
PackageManager mPm;
- PackageParser.Package mPkgInfo;
+ PackageInfo mPkgInfo;
ApplicationInfo mSourceInfo;
// ApplicationInfo object primarily used for already existing applications
@@ -74,38 +83,244 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
// 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";
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
- private static final int DLG_REPLACE_APP = DLG_BASE + 1;
- private static final int DLG_UNKNOWN_APPS = DLG_BASE + 2;
- private static final int DLG_PACKAGE_ERROR = DLG_BASE + 3;
- private static final int DLG_OUT_OF_SPACE = DLG_BASE + 4;
- private static final int DLG_INSTALL_ERROR = DLG_BASE + 5;
- private static final int DLG_ALLOW_SOURCE = DLG_BASE + 6;
+ 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;
+
+ /**
+ * This is a helper class that implements the management of tabs and all
+ * details of connecting a ViewPager with associated TabHost. It relies on a
+ * trick. Normally a tab host has a simple API for supplying a View or
+ * Intent that each tab will show. This is not sufficient for switching
+ * between pages. So instead we make the content part of the tab host
+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
+ * view to show as the tab content. It listens to changes in tabs, and takes
+ * care of switch to the correct paged in the ViewPager whenever the selected
+ * tab changes.
+ */
+ public static class TabsAdapter extends PagerAdapter
+ implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
+ private final Context mContext;
+ private final TabHost mTabHost;
+ private final ViewPager mViewPager;
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ private final Rect mTempRect = new Rect();
+
+ static final class TabInfo {
+ private final String tag;
+ private final View view;
+
+ TabInfo(String _tag, View _view) {
+ tag = _tag;
+ view = _view;
+ }
+ }
+
+ static class DummyTabFactory implements TabHost.TabContentFactory {
+ private final Context mContext;
+
+ public DummyTabFactory(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public View createTabContent(String tag) {
+ View v = new View(mContext);
+ v.setMinimumWidth(0);
+ v.setMinimumHeight(0);
+ return v;
+ }
+ }
+
+ public TabsAdapter(Activity activity, TabHost tabHost, ViewPager pager) {
+ mContext = activity;
+ mTabHost = tabHost;
+ mViewPager = pager;
+ mTabHost.setOnTabChangedListener(this);
+ mViewPager.setAdapter(this);
+ mViewPager.setOnPageChangeListener(this);
+ }
+
+ public void addTab(TabHost.TabSpec tabSpec, View view) {
+ tabSpec.setContent(new DummyTabFactory(mContext));
+ String tag = tabSpec.getTag();
+
+ TabInfo info = new TabInfo(tag, view);
+ mTabs.add(info);
+ mTabHost.addTab(tabSpec);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ View view = mTabs.get(position).view;
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView((View)object);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ @Override
+ public void onTabChanged(String tabId) {
+ int position = mTabHost.getCurrentTab();
+ mViewPager.setCurrentItem(position);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // Unfortunately when TabHost changes the current tab, it kindly
+ // also takes care of putting focus on it when not in touch mode.
+ // The jerk.
+ // This hack tries to prevent this from pulling focus out of our
+ // ViewPager.
+ TabWidget widget = mTabHost.getTabWidget();
+ int oldFocusability = widget.getDescendantFocusability();
+ widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ mTabHost.setCurrentTab(position);
+ widget.setDescendantFocusability(oldFocusability);
+
+ // Scroll the current tab into visibility if needed.
+ View tab = widget.getChildTabViewAt(position);
+ mTempRect.set(tab.getLeft(), tab.getTop(), tab.getRight(), tab.getBottom());
+ widget.requestRectangleOnScreen(mTempRect, false);
+
+ // Make sure the scrollbars are visible for a moment after selection
+ final View contentView = mTabs.get(position).view;
+ if (contentView instanceof CaffeinatedScrollView) {
+ ((CaffeinatedScrollView) contentView).awakenScrollBars();
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+ }
private void startInstallConfirm() {
- LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);
- LinearLayout securityList = (LinearLayout) permsSection.findViewById(
- R.id.security_settings_list);
+ TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
+ tabHost.setup();
+ ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
+ TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
+
boolean permVisible = false;
- if(mPkgInfo != null) {
- AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);
- if(asp.getPermissionCount() > 0) {
+ 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);
+ mScrollView.setFillViewport(true);
+ if (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0) {
+ 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("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;
- securityList.addView(asp.getPermissionsView());
+ 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("all").setIndicator(
+ getText(R.string.allPerms)), root);
+ }
+ }
+ 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);
+ findViewById(R.id.filler).setVisibility(View.VISIBLE);
+ findViewById(R.id.divider).setVisibility(View.GONE);
+ mScrollView = null;
}
- if(!permVisible){
- permsSection.setVisibility(View.INVISIBLE);
+ if (msg != 0) {
+ ((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
}
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) {
@@ -117,27 +332,6 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
@Override
public Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
- case DLG_REPLACE_APP:
- int msgId = R.string.dlg_app_replacement_statement;
- // Customized text for system apps
- if ((mAppInfo != null) && (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- msgId = R.string.dlg_sys_app_replacement_statement;
- }
- return new AlertDialog.Builder(this)
- .setTitle(R.string.dlg_app_replacement_title)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- startInstallConfirm();
- }})
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- Log.i(TAG, "Canceling installation");
- setResult(RESULT_CANCELED);
- finish();
- }})
- .setMessage(msgId)
- .setOnCancelListener(this)
- .create();
case DLG_UNKNOWN_APPS:
return new AlertDialog.Builder(this)
.setTitle(R.string.unknown_apps_dlg_title)
@@ -241,8 +435,8 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
}
private boolean isInstallingUnknownAppsAllowed() {
- return Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
+ return Settings.Global.getInt(getContentResolver(),
+ Settings.Global.INSTALL_NON_MARKET_APPS, 0) > 0;
}
private void initiateInstall() {
@@ -252,22 +446,23 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
- mPkgInfo.setPackageName(pkgName);
+ 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;
}
- if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {
- startInstallConfirm();
- } else {
- if(localLOGV) Log.i(TAG, "Replacing existing package:"+
- mPkgInfo.applicationInfo.packageName);
- showDialogInner(DLG_REPLACE_APP);
- }
+ startInstallConfirm();
}
void setPmResult(int pmResult) {
@@ -284,32 +479,58 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
// get intent information
final Intent intent = getIntent();
mPackageURI = intent.getData();
+ mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+ mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
mPm = getPackageManager();
final String scheme = mPackageURI.getScheme();
- if (scheme != null && !"file".equals(scheme)) {
- throw new IllegalArgumentException("unexpected scheme " + scheme);
+ if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
+ Log.w(TAG, "Unsupported scheme " + scheme);
+ setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
+ return;
}
- final File sourceFile = new File(mPackageURI.getPath());
- mPkgInfo = PackageUtil.getPackageInfo(sourceFile);
+ final PackageUtil.AppSnippet as;
+ if ("package".equals(mPackageURI.getScheme())) {
+ try {
+ 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);
+ return;
+ }
+ as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
+ mPm.getApplicationIcon(mPkgInfo.applicationInfo));
+ } else {
+ final File sourceFile = new File(mPackageURI.getPath());
+ PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
- // Check for parse errors
- if (mPkgInfo == null) {
- Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
- showDialogInner(DLG_PACKAGE_ERROR);
- setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
- return;
+ // 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);
+ return;
+ }
+ mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
+ PackageManager.GET_PERMISSIONS, 0, 0, null,
+ new PackageUserState());
+ as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
}
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
- final PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(
- this, mPkgInfo.applicationInfo, sourceFile);
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
+ mOriginatingUid = getOriginatingUid(intent);
+
// Deal with install source.
String callerPackage = getCallingPackage();
if (callerPackage != null && intent.getBooleanExtra(
@@ -349,7 +570,78 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
}
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_SYSTEM) != 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
+ // 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_SYSTEM) != 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;
+ }
+
// Generic handling when pressing back key
public void onCancel(DialogInterface dialog) {
finish();
@@ -357,44 +649,42 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
public void onClick(View v) {
if(v == mOk) {
- // Start subactivity to actually install the application
- Intent newIntent = new Intent();
- newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
- mPkgInfo.applicationInfo);
- newIntent.setData(mPackageURI);
- newIntent.setClass(this, InstallAppProgress.class);
- String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- 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 (mOkCanInstall || mScrollView == null) {
+ // Start subactivity to actually install the application
+ Intent newIntent = new Intent();
+ newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
+ mPkgInfo.applicationInfo);
+ newIntent.setData(mPackageURI);
+ newIntent.setClass(this, InstallAppProgress.class);
+ 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);
}
- if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
- startActivity(newIntent);
- finish();
} else if(v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
finish();
}
}
-
- /**
- * It's a ScrollView that knows how to stay awake.
- */
- static class CaffeinatedScrollView extends ScrollView {
- public CaffeinatedScrollView(Context context) {
- super(context);
- }
-
- /**
- * Make this visible so we can call it
- */
- @Override
- public boolean awakenScrollBars() {
- return super.awakenScrollBars();
- }
- }
}
diff --git a/src/com/android/packageinstaller/UninstallAppProgress.java b/src/com/android/packageinstaller/UninstallAppProgress.java
index a81d23df..7aa0a2e0 100755
--- a/src/com/android/packageinstaller/UninstallAppProgress.java
+++ b/src/com/android/packageinstaller/UninstallAppProgress.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
@@ -32,7 +33,6 @@ import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import android.content.Context;
/**
* This activity corresponds to a download progress screen that is displayed
@@ -45,6 +45,7 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
private final String TAG="UninstallAppProgress";
private boolean localLOGV = false;
private ApplicationInfo mAppInfo;
+ private boolean mAllUsers;
private TextView mStatusTextView;
private Button mOkButton;
private Button mDeviceManagerButton;
@@ -110,6 +111,7 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
initView();
}
@@ -158,7 +160,8 @@ public class UninstallAppProgress extends Activity implements OnClickListener {
mOkButton.setOnClickListener(this);
mOkPanel.setVisibility(View.INVISIBLE);
PackageDeleteObserver observer = new PackageDeleteObserver();
- getPackageManager().deletePackage(mAppInfo.packageName, observer, 0);
+ getPackageManager().deletePackage(mAppInfo.packageName, observer,
+ mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);
}
public void onClick(View v) {
diff --git a/src/com/android/packageinstaller/UninstallerActivity.java b/src/com/android/packageinstaller/UninstallerActivity.java
index 5a312973..1b9bdce9 100755
--- a/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/src/com/android/packageinstaller/UninstallerActivity.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -30,6 +31,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -52,6 +54,7 @@ public class UninstallerActivity extends Activity implements OnClickListener,
private boolean localLOGV = false;
PackageManager mPm;
private ApplicationInfo mAppInfo;
+ private boolean mAllUsers;
private Button mOk;
private Button mCancel;
@@ -100,6 +103,7 @@ public class UninstallerActivity extends Activity implements OnClickListener,
Intent newIntent = new Intent(Intent.ACTION_VIEW);
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mAppInfo);
+ newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mAllUsers);
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -132,6 +136,8 @@ public class UninstallerActivity extends Activity implements OnClickListener,
errFlag = true;
}
+ mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+
// The class name may have been specified (e.g. when deleting an app from all apps)
String className = packageURI.getFragment();
ActivityInfo activityInfo = null;
@@ -157,7 +163,12 @@ public class UninstallerActivity extends Activity implements OnClickListener,
confirm.setText(R.string.uninstall_update_text);
} else {
setTitle(R.string.uninstall_application_title);
- confirm.setText(R.string.uninstall_application_text);
+ if (mAllUsers && ((UserManager)getSystemService(
+ Context.USER_SERVICE)).getUsers().size() >= 2) {
+ confirm.setText(R.string.uninstall_application_text_all_users);
+ } else {
+ confirm.setText(R.string.uninstall_application_text);
+ }
}
// If an activity was specified (e.g. when dragging from All Apps to trash can),