Add facility to disable system packages.

Change-Id: I762c76993e0f88d255dfc04acbea6c6adea7103d
diff --git a/res/layout/manage_applications_item.xml b/res/layout/manage_applications_item.xml
index 48274ce..5d8d91b 100755
--- a/res/layout/manage_applications_item.xml
+++ b/res/layout/manage_applications_item.xml
@@ -47,12 +47,27 @@
             android:singleLine="true"
             android:ellipsize="marquee"
             android:layout_marginBottom="2dip" />
-        <TextView android:id="@+id/app_size"
-            android:layout_marginTop="-4dip"
-            android:layout_gravity="center_vertical"
-            android:layout_width="wrap_content"
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceSmall" />
+            android:baselineAlignedChildIndex="0" >
+            <TextView android:id="@+id/app_size"
+                android:layout_marginTop="-4dip"
+                android:layout_gravity="center_vertical|left"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+            <TextView android:id="@+id/app_disabled"
+                android:layout_marginTop="-4dip"
+                android:layout_gravity="center_vertical|right"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="0"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:text="@string/disabled" />
+        </LinearLayout>
     </LinearLayout>
 </LinearLayout>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f2300b0..705fdb3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1647,6 +1647,10 @@
     <string name="data_size_label">Data</string>
     <!-- Manage applications, individual application info screen, button label under Storage heading. Button to remove the application from the system. -->
     <string name="uninstall_text">Uninstall</string>
+    <!-- Manage applications, individual application info screen, button label under Storage heading. Button to disable an existing application. -->
+    <string name="disable_text">Disable</string>
+    <!-- Manage applications, individual application info screen, button label under Storage heading. Button to re-enable an existing application. -->
+    <string name="enable_text">Enable</string>
     <!-- Manage applications, individual application info screen, button label under Storage heading. Button to clear all data associated with tis app (for exampel, remove all cached emails for an Email app) -->
     <string name="clear_user_data_text">Clear data</string>
     <!-- Manage applications, restore updated system application to factory version -->
@@ -1681,6 +1685,8 @@
     <!-- Text for filter option in ManageApps screen to display list of
     packages installed on sdcard. -->
     <string name="filter_apps_onsdcard">On SD card</string>
+    <!-- Manage applications, text telling using an application is disabled. -->
+    <string name="disabled">Disabled</string>
     <string name="loading">Loading\u2026</string>
     <!-- Manage app screen, shown when the activity is busy recomputing the size of each app -->
     <string name="recompute_size">Recomputing size\u2026</string>
@@ -1734,7 +1740,7 @@
 
     <string name="force_stop_dlg_title">Force stop</string>
     <!-- Manage applications, text for dialog when killing persistent apps-->
-    <string name="force_stop_dlg_text">This application will be restarted right way. Are you sure you want to force stop?</string>
+    <string name="force_stop_dlg_text">Force stopping an application can cause it to misbehave.  Are you sure?</string>
     <!-- Manage applications, text for dialog when moving an app -->
     <string name="move_app_failed_dlg_title">Move application</string>
     <!-- Manage applications, text for dialog moving an app -->
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 92fe9d2..07a76ab 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -1,5 +1,3 @@
-
-
 /**
  * Copyright (C) 2007 The Android Open Source Project
  *
@@ -42,8 +40,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
+import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -53,6 +53,8 @@
 import android.os.storage.IMountService;
 import android.text.format.Formatter;
 import android.util.Log;
+
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import android.content.ComponentName;
@@ -74,7 +76,6 @@
  */
 public class InstalledAppDetails extends Activity implements View.OnClickListener {
     private static final String TAG="InstalledAppDetails";
-    private static final int _UNKNOWN_APP=R.string.unknown;
     private ApplicationInfo mAppInfo;
     private Button mUninstallButton;
     private boolean mMoveInProgress = false;
@@ -281,11 +282,39 @@
         if (mUpdatedSysApp) {
             mUninstallButton.setText(R.string.app_factory_reset);
         } else {
-            if ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0){
-                // Disable button for system applications.
+            if ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                 enabled = false;
+                try {
+                    // Try to prevent the user from bricking their phone
+                    // by not allowing disabling of apps signed with the
+                    // system cert and any launcher app in the system.
+                    PackageInfo pi = mPm.getPackageInfo(mAppInfo.packageName,
+                            PackageManager.GET_DISABLED_COMPONENTS |
+                            PackageManager.GET_UNINSTALLED_PACKAGES |
+                            PackageManager.GET_SIGNATURES);
+                    PackageInfo sys = mPm.getPackageInfo("android",
+                            PackageManager.GET_SIGNATURES);
+                    Intent intent = new Intent(Intent.ACTION_MAIN);
+                    intent.addCategory(Intent.CATEGORY_HOME);
+                    intent.setPackage(mAppInfo.packageName);
+                    List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
+                    if ((homes != null && homes.size() > 0)
+                            || sys.signatures[0].equals(pi.signatures[0])) {
+                        // Disable button for core system applications.
+                        mUninstallButton.setText(R.string.disable_text);
+                    } else if (mAppInfo.enabled) {
+                        mUninstallButton.setText(R.string.disable_text);
+                        enabled = true;
+                    } else {
+                        mUninstallButton.setText(R.string.enable_text);
+                        enabled = true;
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Unable to get package info", e);
+                }
+            } else {
+                mUninstallButton.setText(R.string.uninstall_text);
             }
-            mUninstallButton.setText(R.string.uninstall_text);
         }
         mUninstallButton.setEnabled(enabled);
         if (enabled) {
@@ -521,6 +550,7 @@
         } else {
             mClearDataButton.setEnabled(true);
         }
+        checkForceStop();
     }
 
     private void refreshButtons() {
@@ -642,13 +672,13 @@
                 }
             })
             .create();
-            case DLG_FORCE_STOP:
-                return new AlertDialog.Builder(this)
-                .setTitle(getString(R.string.force_stop_dlg_title))
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(getString(R.string.force_stop_dlg_text))
-                .setPositiveButton(R.string.dlg_ok,
-                    new DialogInterface.OnClickListener() {
+        case DLG_FORCE_STOP:
+            return new AlertDialog.Builder(this)
+            .setTitle(getString(R.string.force_stop_dlg_title))
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setMessage(getString(R.string.force_stop_dlg_text))
+            .setPositiveButton(R.string.dlg_ok,
+                new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int which) {
                     // Force stop
                     forceStopPackage(mAppInfo.packageName);
@@ -656,15 +686,15 @@
             })
             .setNegativeButton(R.string.dlg_cancel, null)
             .create();
-            case DLG_MOVE_FAILED:
-                CharSequence msg = getString(R.string.move_app_failed_dlg_text,
-                        getMoveErrMsg(mMoveErrorCode));
-                return new AlertDialog.Builder(this)
-                .setTitle(getString(R.string.move_app_failed_dlg_title))
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(msg)
-                .setNeutralButton(R.string.dlg_ok, null)
-                .create();
+        case DLG_MOVE_FAILED:
+            CharSequence msg = getString(R.string.move_app_failed_dlg_text,
+                    getMoveErrMsg(mMoveErrorCode));
+            return new AlertDialog.Builder(this)
+            .setTitle(getString(R.string.move_app_failed_dlg_title))
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setMessage(msg)
+            .setNeutralButton(R.string.dlg_ok, null)
+            .create();
         }
         return null;
     }
@@ -701,6 +731,36 @@
                 Activity.RESULT_CANCELED, null, null);
     }
     
+    static class DisableChanger extends AsyncTask<Object, Object, Object> {
+        final PackageManager mPm;
+        final WeakReference<InstalledAppDetails> mActivity;
+        final ApplicationInfo mInfo;
+        final int mState;
+
+        DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
+            mPm = activity.mPm;
+            mActivity = new WeakReference<InstalledAppDetails>(activity);
+            mInfo = info;
+            mState = state;
+        }
+
+        @Override
+        protected Object doInBackground(Object... params) {
+            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Object result) {
+            InstalledAppDetails activity = mActivity.get();
+            if (activity != null) {
+                activity.initAppInfo(mInfo.packageName);
+                activity.checkForceStop();
+                activity.refreshButtons();
+            }
+        }
+    }
+
     /*
      * Method implementing functionality of buttons clicked
      * @see android.view.View.OnClickListener#onClick(android.view.View)
@@ -711,7 +771,13 @@
             if (mUpdatedSysApp) {
                 showDialogInner(DLG_FACTORY_RESET);
             } else {
-                uninstallPkg(packageName);
+                if ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    new DisableChanger(this, mAppInfo, mAppInfo.enabled ?
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                            : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).execute((Object)null);
+                } else {
+                    uninstallPkg(packageName);
+                }
             }
         } else if(v == mActivitiesButton) {
             mPm.clearPackagePreferredActivities(packageName);
@@ -731,7 +797,8 @@
             }
             mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
         } else if (v == mForceStopButton) {
-            forceStopPackage(mAppInfo.packageName);
+            showDialogInner(DLG_FORCE_STOP);
+            //forceStopPackage(mAppInfo.packageName);
         } else if (v == mMoveAppButton) {
             if (mPackageMoveObserver == null) {
                 mPackageMoveObserver = new PackageMoveObserver();
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 52ea376..03ae68a 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -616,7 +616,8 @@
     
     List<ApplicationInfo> getInstalledApps(int filterOption) {
         List<ApplicationInfo> installedAppList = mPm.getInstalledApplications(
-                PackageManager.GET_UNINSTALLED_PACKAGES);
+                PackageManager.GET_UNINSTALLED_PACKAGES |
+                PackageManager.GET_DISABLED_COMPONENTS);
         if (installedAppList == null) {
             return new ArrayList<ApplicationInfo> ();
         }
@@ -903,6 +904,7 @@
         TextView appName;
         ImageView appIcon;
         TextView appSize;
+        TextView disabled;
     }
     
     /* 
@@ -1076,6 +1078,7 @@
                 holder.appName = (TextView) convertView.findViewById(R.id.app_name);
                 holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
                 holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
+                holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
                 convertView.setTag(holder);
             } else {
                 // Get the ViewHolder back to get fast access to the TextView
@@ -1087,15 +1090,19 @@
             ApplicationInfo appInfo = mAppLocalList.get(position);
             AppInfo mInfo = mCache.getEntry(appInfo.packageName);
             if(mInfo != null) {
-                if(mInfo.appName != null) {
+                if (mInfo.appName != null) {
                     holder.appName.setText(mInfo.appName);
+                    holder.appName.setTextColor(getResources().getColorStateList(
+                            appInfo.enabled ? android.R.color.primary_text_dark
+                                    : android.R.color.secondary_text_dark));
                 }
-                if(mInfo.appIcon != null) {
+                if (mInfo.appIcon != null) {
                     holder.appIcon.setImageDrawable(mInfo.appIcon);
                 }
                 if (mInfo.appSize != null) {
                     holder.appSize.setText(mInfo.appSize);
                 }
+                holder.disabled.setVisibility(appInfo.enabled ? View.GONE : View.VISIBLE);
             } else {
                 Log.w(TAG, "No info for package:"+appInfo.packageName+" in property map");
             }