InstalledAppDetails.java revision c44d7c6be64be790878ddf1f21c0d9e91d238245
1/**
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17package com.android.settings.applications;
18
19import com.android.internal.telephony.ISms;
20import com.android.internal.telephony.SmsUsageMonitor;
21import com.android.settings.R;
22import com.android.settings.Utils;
23import com.android.settings.applications.ApplicationsState.AppEntry;
24
25import android.app.Activity;
26import android.app.ActivityManager;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.DialogFragment;
30import android.app.Fragment;
31import android.app.INotificationManager;
32import android.app.admin.DevicePolicyManager;
33import android.appwidget.AppWidgetManager;
34import android.content.BroadcastReceiver;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.DialogInterface;
38import android.content.Intent;
39import android.content.IntentFilter;
40import android.content.pm.ApplicationInfo;
41import android.content.pm.IPackageDataObserver;
42import android.content.pm.IPackageMoveObserver;
43import android.content.pm.PackageInfo;
44import android.content.pm.PackageManager;
45import android.content.pm.ResolveInfo;
46import android.content.pm.PackageManager.NameNotFoundException;
47import android.content.res.Resources;
48import android.hardware.usb.IUsbManager;
49import android.net.Uri;
50import android.os.AsyncTask;
51import android.os.Bundle;
52import android.os.Environment;
53import android.os.Handler;
54import android.os.IBinder;
55import android.os.Message;
56import android.os.RemoteException;
57import android.os.ServiceManager;
58import android.os.UserHandle;
59import android.os.UserManager;
60import android.preference.PreferenceActivity;
61import android.text.SpannableString;
62import android.text.TextUtils;
63import android.text.format.Formatter;
64import android.text.style.BulletSpan;
65import android.util.Log;
66
67import java.lang.ref.WeakReference;
68import java.util.ArrayList;
69import java.util.HashSet;
70import java.util.List;
71import android.view.LayoutInflater;
72import android.view.Menu;
73import android.view.MenuInflater;
74import android.view.MenuItem;
75import android.view.View;
76import android.view.ViewGroup;
77import android.widget.AdapterView;
78import android.widget.AppSecurityPermissions;
79import android.widget.ArrayAdapter;
80import android.widget.Button;
81import android.widget.CheckBox;
82import android.widget.CompoundButton;
83import android.widget.ImageView;
84import android.widget.LinearLayout;
85import android.widget.Spinner;
86import android.widget.TextView;
87
88/**
89 * Activity to display application information from Settings. This activity presents
90 * extended information associated with a package like code, data, total size, permissions
91 * used by the application and also the set of default launchable activities.
92 * For system applications, an option to clear user data is displayed only if data size is > 0.
93 * System applications that do not want clear user data do not have this option.
94 * For non-system applications, there is no option to clear data. Instead there is an option to
95 * uninstall the application.
96 */
97public class InstalledAppDetails extends Fragment
98        implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
99        ApplicationsState.Callbacks {
100    private static final String TAG="InstalledAppDetails";
101    private static final boolean localLOGV = false;
102
103    public static final String ARG_PACKAGE_NAME = "package";
104
105    private PackageManager mPm;
106    private UserManager mUserManager;
107    private IUsbManager mUsbManager;
108    private AppWidgetManager mAppWidgetManager;
109    private DevicePolicyManager mDpm;
110    private ISms mSmsManager;
111    private ApplicationsState mState;
112    private ApplicationsState.Session mSession;
113    private ApplicationsState.AppEntry mAppEntry;
114    private boolean mInitialized;
115    private boolean mShowUninstalled;
116    private PackageInfo mPackageInfo;
117    private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
118    private View mRootView;
119    private Button mUninstallButton;
120    private View mMoreControlButtons;
121    private Button mSpecialDisableButton;
122    private boolean mMoveInProgress = false;
123    private boolean mUpdatedSysApp = false;
124    private Button mActivitiesButton;
125    private View mScreenCompatSection;
126    private CheckBox mAskCompatibilityCB;
127    private CheckBox mEnableCompatibilityCB;
128    private boolean mCanClearData = true;
129    private TextView mAppVersion;
130    private TextView mTotalSize;
131    private TextView mAppSize;
132    private TextView mDataSize;
133    private TextView mExternalCodeSize;
134    private TextView mExternalDataSize;
135    private ClearUserDataObserver mClearDataObserver;
136    // Views related to cache info
137    private TextView mCacheSize;
138    private Button mClearCacheButton;
139    private ClearCacheObserver mClearCacheObserver;
140    private Button mForceStopButton;
141    private Button mClearDataButton;
142    private Button mMoveAppButton;
143    private CompoundButton mNotificationSwitch;
144
145    private PackageMoveObserver mPackageMoveObserver;
146
147    private final HashSet<String> mHomePackages = new HashSet<String>();
148
149    private boolean mDisableAfterUninstall;
150
151    private boolean mHaveSizes = false;
152    private long mLastCodeSize = -1;
153    private long mLastDataSize = -1;
154    private long mLastExternalCodeSize = -1;
155    private long mLastExternalDataSize = -1;
156    private long mLastCacheSize = -1;
157    private long mLastTotalSize = -1;
158
159    //internal constants used in Handler
160    private static final int OP_SUCCESSFUL = 1;
161    private static final int OP_FAILED = 2;
162    private static final int CLEAR_USER_DATA = 1;
163    private static final int CLEAR_CACHE = 3;
164    private static final int PACKAGE_MOVE = 4;
165
166    // invalid size value used initially and also when size retrieval through PackageManager
167    // fails for whatever reason
168    private static final int SIZE_INVALID = -1;
169
170    // Resource strings
171    private CharSequence mInvalidSizeStr;
172    private CharSequence mComputingStr;
173
174    // Dialog identifiers used in showDialog
175    private static final int DLG_BASE = 0;
176    private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
177    private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
178    private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
179    private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
180    private static final int DLG_FORCE_STOP = DLG_BASE + 5;
181    private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
182    private static final int DLG_DISABLE = DLG_BASE + 7;
183    private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
184    private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9;
185
186    // Menu identifiers
187    public static final int UNINSTALL_ALL_USERS_MENU = 1;
188
189    // Result code identifiers
190    public static final int REQUEST_UNINSTALL = 1;
191    public static final int REQUEST_MANAGE_SPACE = 2;
192
193    private Handler mHandler = new Handler() {
194        public void handleMessage(Message msg) {
195            // If the fragment is gone, don't process any more messages.
196            if (getView() == null) {
197                return;
198            }
199            switch (msg.what) {
200                case CLEAR_USER_DATA:
201                    processClearMsg(msg);
202                    break;
203                case CLEAR_CACHE:
204                    // Refresh size info
205                    mState.requestSize(mAppEntry.info.packageName);
206                    break;
207                case PACKAGE_MOVE:
208                    processMoveMsg(msg);
209                    break;
210                default:
211                    break;
212            }
213        }
214    };
215
216    class ClearUserDataObserver extends IPackageDataObserver.Stub {
217       public void onRemoveCompleted(final String packageName, final boolean succeeded) {
218           final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
219           msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
220           mHandler.sendMessage(msg);
221        }
222    }
223
224    class ClearCacheObserver extends IPackageDataObserver.Stub {
225        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
226            final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
227            msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
228            mHandler.sendMessage(msg);
229         }
230     }
231
232    class PackageMoveObserver extends IPackageMoveObserver.Stub {
233        public void packageMoved(String packageName, int returnCode) throws RemoteException {
234            final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
235            msg.arg1 = returnCode;
236            mHandler.sendMessage(msg);
237        }
238    }
239
240    private String getSizeStr(long size) {
241        if (size == SIZE_INVALID) {
242            return mInvalidSizeStr.toString();
243        }
244        return Formatter.formatFileSize(getActivity(), size);
245    }
246
247    private void initDataButtons() {
248        // If the app doesn't have its own space management UI
249        // And it's a system app that doesn't allow clearing user data or is an active admin
250        // Then disable the Clear Data button.
251        if (mAppEntry.info.manageSpaceActivityName == null
252                && ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
253                        | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
254                        == ApplicationInfo.FLAG_SYSTEM
255                        || mDpm.packageHasActiveAdmins(mPackageInfo.packageName))) {
256            mClearDataButton.setText(R.string.clear_user_data_text);
257            mClearDataButton.setEnabled(false);
258            mCanClearData = false;
259        } else {
260            if (mAppEntry.info.manageSpaceActivityName != null) {
261                mClearDataButton.setText(R.string.manage_space_text);
262            } else {
263                mClearDataButton.setText(R.string.clear_user_data_text);
264            }
265            mClearDataButton.setOnClickListener(this);
266        }
267    }
268
269    private CharSequence getMoveErrMsg(int errCode) {
270        switch (errCode) {
271            case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
272                return getActivity().getString(R.string.insufficient_storage);
273            case PackageManager.MOVE_FAILED_DOESNT_EXIST:
274                return getActivity().getString(R.string.does_not_exist);
275            case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
276                return getActivity().getString(R.string.app_forward_locked);
277            case PackageManager.MOVE_FAILED_INVALID_LOCATION:
278                return getActivity().getString(R.string.invalid_location);
279            case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
280                return getActivity().getString(R.string.system_package);
281            case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
282                return "";
283        }
284        return "";
285    }
286
287    private void initMoveButton() {
288        if (Environment.isExternalStorageEmulated()) {
289            mMoveAppButton.setVisibility(View.INVISIBLE);
290            return;
291        }
292        boolean dataOnly = false;
293        dataOnly = (mPackageInfo == null) && (mAppEntry != null);
294        boolean moveDisable = true;
295        if (dataOnly) {
296            mMoveAppButton.setText(R.string.move_app);
297        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
298            mMoveAppButton.setText(R.string.move_app_to_internal);
299            // Always let apps move to internal storage from sdcard.
300            moveDisable = false;
301        } else {
302            mMoveAppButton.setText(R.string.move_app_to_sdcard);
303            mCanBeOnSdCardChecker.init();
304            moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
305        }
306        if (moveDisable) {
307            mMoveAppButton.setEnabled(false);
308        } else {
309            mMoveAppButton.setOnClickListener(this);
310            mMoveAppButton.setEnabled(true);
311        }
312    }
313
314    private boolean isThisASystemPackage() {
315        try {
316            PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
317            return (mPackageInfo != null && mPackageInfo.signatures != null &&
318                    sys.signatures[0].equals(mPackageInfo.signatures[0]));
319        } catch (PackageManager.NameNotFoundException e) {
320            return false;
321        }
322    }
323
324    private boolean handleDisableable(Button button) {
325        boolean disableable = false;
326        // Try to prevent the user from bricking their phone
327        // by not allowing disabling of apps signed with the
328        // system cert and any launcher app in the system.
329        if (mHomePackages.contains(mAppEntry.info.packageName) || isThisASystemPackage()) {
330            // Disable button for core system applications.
331            button.setText(R.string.disable_text);
332        } else if (mAppEntry.info.enabled) {
333            button.setText(R.string.disable_text);
334            disableable = true;
335        } else {
336            button.setText(R.string.enable_text);
337            disableable = true;
338        }
339
340        return disableable;
341    }
342
343    private void initUninstallButtons() {
344        mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
345        boolean enabled = true;
346        if (mUpdatedSysApp) {
347            mUninstallButton.setText(R.string.app_factory_reset);
348            boolean specialDisable = false;
349            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
350                specialDisable = handleDisableable(mSpecialDisableButton);
351                mSpecialDisableButton.setOnClickListener(this);
352            }
353            mMoreControlButtons.setVisibility(specialDisable ? View.VISIBLE : View.GONE);
354        } else {
355            mMoreControlButtons.setVisibility(View.GONE);
356            if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
357                enabled = handleDisableable(mUninstallButton);
358            } else if ((mPackageInfo.applicationInfo.flags
359                    & ApplicationInfo.FLAG_INSTALLED) == 0
360                    && mUserManager.getUsers().size() >= 2) {
361                // When we have multiple users, there is a separate menu
362                // to uninstall for all users.
363                mUninstallButton.setText(R.string.uninstall_text);
364                enabled = false;
365            } else {
366                mUninstallButton.setText(R.string.uninstall_text);
367            }
368        }
369        // If this is a device admin, it can't be uninstalled or disabled.
370        // We do this here so the text of the button is still set correctly.
371        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
372            enabled = false;
373        }
374
375        // If this is the default (or only) home app, suppress uninstall (even if
376        // we still think it should be allowed for other reasons)
377        if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
378            ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
379            ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
380            if (currentDefaultHome == null) {
381                // No preferred default, so permit uninstall only when
382                // there is more than one candidate
383                enabled = (mHomePackages.size() > 1);
384            } else {
385                // There is an explicit default home app -- forbid uninstall of
386                // that one, but permit it for installed-but-inactive ones.
387                enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
388            }
389        }
390
391        mUninstallButton.setEnabled(enabled);
392        if (enabled) {
393            // Register listener
394            mUninstallButton.setOnClickListener(this);
395        }
396    }
397
398    private void initNotificationButton() {
399        INotificationManager nm = INotificationManager.Stub.asInterface(
400                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
401        boolean enabled = true; // default on
402        try {
403            enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName,
404                    mAppEntry.info.uid);
405        } catch (android.os.RemoteException ex) {
406            // this does not bode well
407        }
408        mNotificationSwitch.setChecked(enabled);
409        if (isThisASystemPackage()) {
410            mNotificationSwitch.setEnabled(false);
411        } else {
412            mNotificationSwitch.setEnabled(true);
413            mNotificationSwitch.setOnCheckedChangeListener(this);
414        }
415    }
416
417    /** Called when the activity is first created. */
418    @Override
419    public void onCreate(Bundle icicle) {
420        super.onCreate(icicle);
421
422        mState = ApplicationsState.getInstance(getActivity().getApplication());
423        mSession = mState.newSession(this);
424        mPm = getActivity().getPackageManager();
425        mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
426        IBinder b = ServiceManager.getService(Context.USB_SERVICE);
427        mUsbManager = IUsbManager.Stub.asInterface(b);
428        mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
429        mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
430        mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
431
432        mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
433
434        // Need to make sure we have loaded applications at this point.
435        mSession.resume();
436
437        retrieveAppEntry();
438
439        setHasOptionsMenu(true);
440    }
441
442    @Override
443    public View onCreateView(
444            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
445        final View view = inflater.inflate(R.layout.installed_app_details, container, false);
446        Utils.prepareCustomPreferencesList(container, view, view, false);
447
448        mRootView = view;
449        mComputingStr = getActivity().getText(R.string.computing_size);
450
451        // Set default values on sizes
452        mTotalSize = (TextView)view.findViewById(R.id.total_size_text);
453        mAppSize = (TextView)view.findViewById(R.id.application_size_text);
454        mDataSize = (TextView)view.findViewById(R.id.data_size_text);
455        mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text);
456        mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text);
457
458        if (Environment.isExternalStorageEmulated()) {
459            ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE);
460            ((View)mExternalDataSize.getParent()).setVisibility(View.GONE);
461        }
462
463        // Get Control button panel
464        View btnPanel = view.findViewById(R.id.control_buttons_panel);
465        mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
466        mForceStopButton.setText(R.string.force_stop);
467        mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
468        mForceStopButton.setEnabled(false);
469
470        // Get More Control button panel
471        mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel);
472        mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
473        mSpecialDisableButton = (Button)mMoreControlButtons.findViewById(R.id.right_button);
474        mMoreControlButtons.setVisibility(View.GONE);
475
476        // Initialize clear data and move install location buttons
477        View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
478        mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
479        mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
480
481        // Cache section
482        mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
483        mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
484
485        mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button);
486
487        // Screen compatibility control
488        mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
489        mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
490        mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
491
492        mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
493
494        return view;
495    }
496
497    @Override
498    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
499        menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
500                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
501    }
502
503    @Override
504    public void onPrepareOptionsMenu(Menu menu) {
505        boolean showIt = true;
506        if (mUpdatedSysApp) {
507            showIt = false;
508        } else if (mAppEntry == null) {
509            showIt = false;
510        } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
511            showIt = false;
512        } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
513            showIt = false;
514        } else if (UserHandle.myUserId() != 0) {
515            showIt = false;
516        } else if (mUserManager.getUsers().size() < 2) {
517            showIt = false;
518        }
519        menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
520    }
521
522    @Override
523    public boolean onOptionsItemSelected(MenuItem item) {
524        int menuId = item.getItemId();
525        if (menuId == UNINSTALL_ALL_USERS_MENU) {
526            uninstallPkg(mAppEntry.info.packageName, true, false);
527            return true;
528        }
529        return false;
530    }
531
532    @Override
533    public void onActivityResult(int requestCode, int resultCode, Intent data) {
534        super.onActivityResult(requestCode, resultCode, data);
535        if (requestCode == REQUEST_UNINSTALL) {
536            if (mDisableAfterUninstall) {
537                mDisableAfterUninstall = false;
538                try {
539                    ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
540                            mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
541                            | PackageManager.GET_DISABLED_COMPONENTS);
542                    if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
543                        new DisableChanger(this, mAppEntry.info,
544                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
545                                .execute((Object)null);
546                    }
547                } catch (NameNotFoundException e) {
548                }
549            }
550            if (!refreshUi()) {
551                setIntentAndFinish(true, true);
552            }
553        }
554    }
555
556    // Utility method to set application label and icon.
557    private void setAppLabelAndIcon(PackageInfo pkgInfo) {
558        final View appSnippet = mRootView.findViewById(R.id.app_snippet);
559        appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom());
560
561        ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
562        mState.ensureIcon(mAppEntry);
563        icon.setImageDrawable(mAppEntry.icon);
564        // Set application name.
565        TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
566        label.setText(mAppEntry.label);
567        // Version number of application
568        mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
569
570        if (pkgInfo != null && pkgInfo.versionName != null) {
571            mAppVersion.setVisibility(View.VISIBLE);
572            mAppVersion.setText(getActivity().getString(R.string.version_text,
573                    String.valueOf(pkgInfo.versionName)));
574        } else {
575            mAppVersion.setVisibility(View.INVISIBLE);
576        }
577    }
578
579    @Override
580    public void onResume() {
581        super.onResume();
582
583        mSession.resume();
584        if (!refreshUi()) {
585            setIntentAndFinish(true, true);
586        }
587    }
588
589    @Override
590    public void onPause() {
591        super.onPause();
592        mSession.pause();
593    }
594
595    @Override
596    public void onAllSizesComputed() {
597    }
598
599    @Override
600    public void onPackageIconChanged() {
601    }
602
603    @Override
604    public void onPackageListChanged() {
605        refreshUi();
606    }
607
608    @Override
609    public void onRebuildComplete(ArrayList<AppEntry> apps) {
610    }
611
612    @Override
613    public void onPackageSizeChanged(String packageName) {
614        if (packageName.equals(mAppEntry.info.packageName)) {
615            refreshSizeInfo();
616        }
617    }
618
619    @Override
620    public void onRunningStateChanged(boolean running) {
621    }
622
623    private String retrieveAppEntry() {
624        final Bundle args = getArguments();
625        String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
626        if (packageName == null) {
627            Intent intent = (args == null) ?
628                    getActivity().getIntent() : (Intent) args.getParcelable("intent");
629            if (intent != null) {
630                packageName = intent.getData().getSchemeSpecificPart();
631            }
632        }
633        mAppEntry = mState.getEntry(packageName);
634        if (mAppEntry != null) {
635            // Get application info again to refresh changed properties of application
636            try {
637                mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
638                        PackageManager.GET_DISABLED_COMPONENTS |
639                        PackageManager.GET_UNINSTALLED_PACKAGES |
640                        PackageManager.GET_SIGNATURES);
641            } catch (NameNotFoundException e) {
642                Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
643            }
644        } else {
645            Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
646            mPackageInfo = null;
647        }
648
649        return packageName;
650    }
651
652    private boolean signaturesMatch(String pkg1, String pkg2) {
653        if (pkg1 != null && pkg2 != null) {
654            try {
655                final int match = mPm.checkSignatures(pkg1, pkg2);
656                if (match >= PackageManager.SIGNATURE_MATCH) {
657                    return true;
658                }
659            } catch (Exception e) {
660                // e.g. named alternate package not found during lookup;
661                // this is an expected case sometimes
662            }
663        }
664        return false;
665    }
666
667    private boolean refreshUi() {
668        if (mMoveInProgress) {
669            return true;
670        }
671        final String packageName = retrieveAppEntry();
672
673        if (mAppEntry == null) {
674            return false; // onCreate must have failed, make sure to exit
675        }
676
677        if (mPackageInfo == null) {
678            return false; // onCreate must have failed, make sure to exit
679        }
680
681        // Get list of "home" apps and trace through any meta-data references
682        List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
683        mPm.getHomeActivities(homeActivities);
684        mHomePackages.clear();
685        for (int i = 0; i< homeActivities.size(); i++) {
686            ResolveInfo ri = homeActivities.get(i);
687            final String activityPkg = ri.activityInfo.packageName;
688            mHomePackages.add(activityPkg);
689
690            // Also make sure to include anything proxying for the home app
691            final Bundle metadata = ri.activityInfo.metaData;
692            if (metadata != null) {
693                final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
694                if (signaturesMatch(metaPkg, activityPkg)) {
695                    mHomePackages.add(metaPkg);
696                }
697            }
698        }
699
700        // Get list of preferred activities
701        List<ComponentName> prefActList = new ArrayList<ComponentName>();
702
703        // Intent list cannot be null. so pass empty list
704        List<IntentFilter> intentList = new ArrayList<IntentFilter>();
705        mPm.getPreferredActivities(intentList, prefActList, packageName);
706        if (localLOGV)
707            Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
708        boolean hasUsbDefaults = false;
709        try {
710            hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId());
711        } catch (RemoteException e) {
712            Log.e(TAG, "mUsbManager.hasDefaults", e);
713        }
714        boolean hasBindAppWidgetPermission =
715                mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
716
717        TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
718        TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
719        boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
720        if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
721            resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
722        } else {
723            boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
724
725            if (hasBindAppWidgetPermission) {
726                autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
727            } else {
728                autoLaunchTitleView.setText(R.string.auto_launch_label);
729            }
730
731            CharSequence text = null;
732            int bulletIndent = getResources()
733                    .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
734            if (autoLaunchEnabled) {
735                CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
736                SpannableString s = new SpannableString(autoLaunchEnableText);
737                if (useBullets) {
738                    s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
739                }
740                text = (text == null) ?
741                        TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
742            }
743            if (hasBindAppWidgetPermission) {
744                CharSequence alwaysAllowBindAppWidgetsText =
745                        getText(R.string.always_allow_bind_appwidgets_text);
746                SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
747                if (useBullets) {
748                    s.setSpan(new BulletSpan(bulletIndent),
749                            0, alwaysAllowBindAppWidgetsText.length(), 0);
750                }
751                text = (text == null) ?
752                        TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
753            }
754            autoLaunchView.setText(text);
755            mActivitiesButton.setEnabled(true);
756            mActivitiesButton.setOnClickListener(this);
757        }
758
759        // Screen compatibility section.
760        ActivityManager am = (ActivityManager)
761                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
762        int compatMode = am.getPackageScreenCompatMode(packageName);
763        // For now these are always off; this is the old UI model which we
764        // are no longer using.
765        if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
766                || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
767            mScreenCompatSection.setVisibility(View.VISIBLE);
768            mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
769            mAskCompatibilityCB.setOnCheckedChangeListener(this);
770            mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED);
771            mEnableCompatibilityCB.setOnCheckedChangeListener(this);
772        } else {
773            mScreenCompatSection.setVisibility(View.GONE);
774        }
775
776        // Security permissions section
777        LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
778        AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
779        int premiumSmsPermission = getPremiumSmsPermission(packageName);
780        // Premium SMS permission implies the app also has SEND_SMS permission, so the original
781        // application permissions list doesn't have to be shown/hidden separately. The premium
782        // SMS subsection should only be visible if the app has tried to send to a premium SMS.
783        if (asp.getPermissionCount() > 0
784                || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
785            permsView.setVisibility(View.VISIBLE);
786        } else {
787            permsView.setVisibility(View.GONE);
788        }
789        // Premium SMS permission subsection
790        TextView securityBillingDesc = (TextView) permsView.findViewById(
791                R.id.security_settings_billing_desc);
792        LinearLayout securityBillingList = (LinearLayout) permsView.findViewById(
793                R.id.security_settings_billing_list);
794        if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
795            // Show the premium SMS permission selector
796            securityBillingDesc.setVisibility(View.VISIBLE);
797            securityBillingList.setVisibility(View.VISIBLE);
798            Spinner spinner = (Spinner) permsView.findViewById(
799                    R.id.security_settings_premium_sms_list);
800            ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
801                    R.array.security_settings_premium_sms_values,
802                    android.R.layout.simple_spinner_item);
803            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
804            spinner.setAdapter(adapter);
805            // List items are in the same order as SmsUsageMonitor constants, offset by 1.
806            spinner.setSelection(premiumSmsPermission - 1);
807            spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener(
808                    packageName, mSmsManager));
809        } else {
810            // Hide the premium SMS permission selector
811            securityBillingDesc.setVisibility(View.GONE);
812            securityBillingList.setVisibility(View.GONE);
813        }
814        // App permissions subsection
815        if (asp.getPermissionCount() > 0) {
816            // Make the security sections header visible
817            LinearLayout securityList = (LinearLayout) permsView.findViewById(
818                    R.id.security_settings_list);
819            securityList.removeAllViews();
820            securityList.addView(asp.getPermissionsViewWithRevokeButtons());
821            // If this app is running under a shared user ID with other apps,
822            // update the description to explain this.
823            String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid);
824            if (packages != null && packages.length > 1) {
825                ArrayList<CharSequence> pnames = new ArrayList<CharSequence>();
826                for (int i=0; i<packages.length; i++) {
827                    String pkg = packages[i];
828                    if (mPackageInfo.packageName.equals(pkg)) {
829                        continue;
830                    }
831                    try {
832                        ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0);
833                        pnames.add(ainfo.loadLabel(mPm));
834                    } catch (PackageManager.NameNotFoundException e) {
835                    }
836                }
837                final int N = pnames.size();
838                if (N > 0) {
839                    final Resources res = getActivity().getResources();
840                    String appListStr;
841                    if (N == 1) {
842                        appListStr = pnames.get(0).toString();
843                    } else if (N == 2) {
844                        appListStr = res.getString(R.string.join_two_items, pnames.get(0),
845                                pnames.get(1));
846                    } else {
847                        appListStr = pnames.get(N-2).toString();
848                        for (int i=N-3; i>=0; i--) {
849                            appListStr = res.getString(i == 0 ? R.string.join_many_items_first
850                                    : R.string.join_many_items_middle, pnames.get(i), appListStr);
851                        }
852                        appListStr = res.getString(R.string.join_many_items_last,
853                                appListStr, pnames.get(N-1));
854                    }
855                    TextView descr = (TextView) mRootView.findViewById(
856                            R.id.security_settings_desc);
857                    descr.setText(res.getString(R.string.security_settings_desc_multi,
858                            mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
859                }
860            }
861        }
862
863        checkForceStop();
864        setAppLabelAndIcon(mPackageInfo);
865        refreshButtons();
866        refreshSizeInfo();
867
868        if (!mInitialized) {
869            // First time init: are we displaying an uninstalled app?
870            mInitialized = true;
871            mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
872        } else {
873            // All other times: if the app no longer exists then we want
874            // to go away.
875            try {
876                ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
877                        mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
878                        | PackageManager.GET_DISABLED_COMPONENTS);
879                if (!mShowUninstalled) {
880                    // If we did not start out with the app uninstalled, then
881                    // it transitioning to the uninstalled state for the current
882                    // user means we should go away as well.
883                    return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
884                }
885            } catch (NameNotFoundException e) {
886                return false;
887            }
888        }
889
890        return true;
891    }
892
893    private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener {
894        private final String mPackageName;
895        private final ISms mSmsManager;
896
897        PremiumSmsSelectionListener(String packageName, ISms smsManager) {
898            mPackageName = packageName;
899            mSmsManager = smsManager;
900        }
901
902        @Override
903        public void onItemSelected(AdapterView<?> parent, View view, int position,
904                long id) {
905            if (position >= 0 && position < 3) {
906                Log.d(TAG, "Selected premium SMS policy " + position);
907                setPremiumSmsPermission(mPackageName, (position + 1));
908            } else {
909                Log.e(TAG, "Error: unknown premium SMS policy " + position);
910            }
911        }
912
913        @Override
914        public void onNothingSelected(AdapterView<?> parent) {
915            // Ignored
916        }
917
918        private void setPremiumSmsPermission(String packageName, int permission) {
919            try {
920                if (mSmsManager != null) {
921                    mSmsManager.setPremiumSmsPermission(packageName, permission);
922                }
923            } catch (RemoteException ex) {
924                // ignored
925            }
926        }
927    }
928
929    private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
930        title.setText(R.string.auto_launch_label);
931        autoLaunchView.setText(R.string.auto_launch_disable_text);
932        // Disable clear activities button
933        mActivitiesButton.setEnabled(false);
934    }
935
936    private void setIntentAndFinish(boolean finish, boolean appChanged) {
937        if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
938        Intent intent = new Intent();
939        intent.putExtra(ManageApplications.APP_CHG, appChanged);
940        PreferenceActivity pa = (PreferenceActivity)getActivity();
941        pa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
942    }
943
944    private void refreshSizeInfo() {
945        if (mAppEntry.size == ApplicationsState.SIZE_INVALID
946                || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
947            mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
948            if (!mHaveSizes) {
949                mAppSize.setText(mComputingStr);
950                mDataSize.setText(mComputingStr);
951                mCacheSize.setText(mComputingStr);
952                mTotalSize.setText(mComputingStr);
953            }
954            mClearDataButton.setEnabled(false);
955            mClearCacheButton.setEnabled(false);
956
957        } else {
958            mHaveSizes = true;
959            long codeSize = mAppEntry.codeSize;
960            long dataSize = mAppEntry.dataSize;
961            if (Environment.isExternalStorageEmulated()) {
962                codeSize += mAppEntry.externalCodeSize;
963                dataSize +=  mAppEntry.externalDataSize;
964            } else {
965                if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
966                    mLastExternalCodeSize = mAppEntry.externalCodeSize;
967                    mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize));
968                }
969                if (mLastExternalDataSize !=  mAppEntry.externalDataSize) {
970                    mLastExternalDataSize =  mAppEntry.externalDataSize;
971                    mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize));
972                }
973            }
974            if (mLastCodeSize != codeSize) {
975                mLastCodeSize = codeSize;
976                mAppSize.setText(getSizeStr(codeSize));
977            }
978            if (mLastDataSize != dataSize) {
979                mLastDataSize = dataSize;
980                mDataSize.setText(getSizeStr(dataSize));
981            }
982            long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
983            if (mLastCacheSize != cacheSize) {
984                mLastCacheSize = cacheSize;
985                mCacheSize.setText(getSizeStr(cacheSize));
986            }
987            if (mLastTotalSize != mAppEntry.size) {
988                mLastTotalSize = mAppEntry.size;
989                mTotalSize.setText(getSizeStr(mAppEntry.size));
990            }
991
992            if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
993                mClearDataButton.setEnabled(false);
994            } else {
995                mClearDataButton.setEnabled(true);
996                mClearDataButton.setOnClickListener(this);
997            }
998            if (cacheSize <= 0) {
999                mClearCacheButton.setEnabled(false);
1000            } else {
1001                mClearCacheButton.setEnabled(true);
1002                mClearCacheButton.setOnClickListener(this);
1003            }
1004        }
1005    }
1006
1007    /*
1008     * Private method to handle clear message notification from observer when
1009     * the async operation from PackageManager is complete
1010     */
1011    private void processClearMsg(Message msg) {
1012        int result = msg.arg1;
1013        String packageName = mAppEntry.info.packageName;
1014        mClearDataButton.setText(R.string.clear_user_data_text);
1015        if(result == OP_SUCCESSFUL) {
1016            Log.i(TAG, "Cleared user data for package : "+packageName);
1017            mState.requestSize(mAppEntry.info.packageName);
1018        } else {
1019            mClearDataButton.setEnabled(true);
1020        }
1021        checkForceStop();
1022    }
1023
1024    private void refreshButtons() {
1025        if (!mMoveInProgress) {
1026            initUninstallButtons();
1027            initDataButtons();
1028            initMoveButton();
1029            initNotificationButton();
1030        } else {
1031            mMoveAppButton.setText(R.string.moving);
1032            mMoveAppButton.setEnabled(false);
1033            mUninstallButton.setEnabled(false);
1034            mSpecialDisableButton.setEnabled(false);
1035        }
1036    }
1037
1038    private void processMoveMsg(Message msg) {
1039        int result = msg.arg1;
1040        String packageName = mAppEntry.info.packageName;
1041        // Refresh the button attributes.
1042        mMoveInProgress = false;
1043        if (result == PackageManager.MOVE_SUCCEEDED) {
1044            Log.i(TAG, "Moved resources for " + packageName);
1045            // Refresh size information again.
1046            mState.requestSize(mAppEntry.info.packageName);
1047        } else {
1048            showDialogInner(DLG_MOVE_FAILED, result);
1049        }
1050        refreshUi();
1051    }
1052
1053    /*
1054     * Private method to initiate clearing user data when the user clicks the clear data
1055     * button for a system package
1056     */
1057    private  void initiateClearUserData() {
1058        mClearDataButton.setEnabled(false);
1059        // Invoke uninstall or clear user data based on sysPackage
1060        String packageName = mAppEntry.info.packageName;
1061        Log.i(TAG, "Clearing user data for package : " + packageName);
1062        if (mClearDataObserver == null) {
1063            mClearDataObserver = new ClearUserDataObserver();
1064        }
1065        ActivityManager am = (ActivityManager)
1066                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1067        boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
1068        if (!res) {
1069            // Clearing data failed for some obscure reason. Just log error for now
1070            Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
1071            showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
1072        } else {
1073            mClearDataButton.setText(R.string.recompute_size);
1074        }
1075    }
1076
1077    private void showDialogInner(int id, int moveErrorCode) {
1078        DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
1079        newFragment.setTargetFragment(this, 0);
1080        newFragment.show(getFragmentManager(), "dialog " + id);
1081    }
1082
1083    public static class MyAlertDialogFragment extends DialogFragment {
1084
1085        public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
1086            MyAlertDialogFragment frag = new MyAlertDialogFragment();
1087            Bundle args = new Bundle();
1088            args.putInt("id", id);
1089            args.putInt("moveError", moveErrorCode);
1090            frag.setArguments(args);
1091            return frag;
1092        }
1093
1094        InstalledAppDetails getOwner() {
1095            return (InstalledAppDetails)getTargetFragment();
1096        }
1097
1098        @Override
1099        public Dialog onCreateDialog(Bundle savedInstanceState) {
1100            int id = getArguments().getInt("id");
1101            int moveErrorCode = getArguments().getInt("moveError");
1102            switch (id) {
1103                case DLG_CLEAR_DATA:
1104                    return new AlertDialog.Builder(getActivity())
1105                    .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
1106                    .setIconAttribute(android.R.attr.alertDialogIcon)
1107                    .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
1108                    .setPositiveButton(R.string.dlg_ok,
1109                            new DialogInterface.OnClickListener() {
1110                        public void onClick(DialogInterface dialog, int which) {
1111                            // Clear user data here
1112                            getOwner().initiateClearUserData();
1113                        }
1114                    })
1115                    .setNegativeButton(R.string.dlg_cancel, null)
1116                    .create();
1117                case DLG_FACTORY_RESET:
1118                    return new AlertDialog.Builder(getActivity())
1119                    .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
1120                    .setIconAttribute(android.R.attr.alertDialogIcon)
1121                    .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
1122                    .setPositiveButton(R.string.dlg_ok,
1123                            new DialogInterface.OnClickListener() {
1124                        public void onClick(DialogInterface dialog, int which) {
1125                            // Clear user data here
1126                            getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1127                                    false, false);
1128                        }
1129                    })
1130                    .setNegativeButton(R.string.dlg_cancel, null)
1131                    .create();
1132                case DLG_APP_NOT_FOUND:
1133                    return new AlertDialog.Builder(getActivity())
1134                    .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
1135                    .setIconAttribute(android.R.attr.alertDialogIcon)
1136                    .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
1137                    .setNeutralButton(getActivity().getText(R.string.dlg_ok),
1138                            new DialogInterface.OnClickListener() {
1139                        public void onClick(DialogInterface dialog, int which) {
1140                            //force to recompute changed value
1141                            getOwner().setIntentAndFinish(true, true);
1142                        }
1143                    })
1144                    .create();
1145                case DLG_CANNOT_CLEAR_DATA:
1146                    return new AlertDialog.Builder(getActivity())
1147                    .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
1148                    .setIconAttribute(android.R.attr.alertDialogIcon)
1149                    .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
1150                    .setNeutralButton(R.string.dlg_ok,
1151                            new DialogInterface.OnClickListener() {
1152                        public void onClick(DialogInterface dialog, int which) {
1153                            getOwner().mClearDataButton.setEnabled(false);
1154                            //force to recompute changed value
1155                            getOwner().setIntentAndFinish(false, false);
1156                        }
1157                    })
1158                    .create();
1159                case DLG_FORCE_STOP:
1160                    return new AlertDialog.Builder(getActivity())
1161                    .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
1162                    .setIconAttribute(android.R.attr.alertDialogIcon)
1163                    .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
1164                    .setPositiveButton(R.string.dlg_ok,
1165                        new DialogInterface.OnClickListener() {
1166                        public void onClick(DialogInterface dialog, int which) {
1167                            // Force stop
1168                            getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
1169                        }
1170                    })
1171                    .setNegativeButton(R.string.dlg_cancel, null)
1172                    .create();
1173                case DLG_MOVE_FAILED:
1174                    CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
1175                            getOwner().getMoveErrMsg(moveErrorCode));
1176                    return new AlertDialog.Builder(getActivity())
1177                    .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
1178                    .setIconAttribute(android.R.attr.alertDialogIcon)
1179                    .setMessage(msg)
1180                    .setNeutralButton(R.string.dlg_ok, null)
1181                    .create();
1182                case DLG_DISABLE:
1183                    return new AlertDialog.Builder(getActivity())
1184                    .setTitle(getActivity().getText(R.string.app_disable_dlg_title))
1185                    .setIconAttribute(android.R.attr.alertDialogIcon)
1186                    .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
1187                    .setPositiveButton(R.string.dlg_ok,
1188                        new DialogInterface.OnClickListener() {
1189                        public void onClick(DialogInterface dialog, int which) {
1190                            // Disable the app
1191                            new DisableChanger(getOwner(), getOwner().mAppEntry.info,
1192                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
1193                            .execute((Object)null);
1194                        }
1195                    })
1196                    .setNegativeButton(R.string.dlg_cancel, null)
1197                    .create();
1198                case DLG_DISABLE_NOTIFICATIONS:
1199                    return new AlertDialog.Builder(getActivity())
1200                    .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
1201                    .setIconAttribute(android.R.attr.alertDialogIcon)
1202                    .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
1203                    .setPositiveButton(R.string.dlg_ok,
1204                        new DialogInterface.OnClickListener() {
1205                        public void onClick(DialogInterface dialog, int which) {
1206                            // Disable the package's notifications
1207                            getOwner().setNotificationsEnabled(false);
1208                        }
1209                    })
1210                    .setNegativeButton(R.string.dlg_cancel,
1211                        new DialogInterface.OnClickListener() {
1212                        public void onClick(DialogInterface dialog, int which) {
1213                            // Re-enable the checkbox
1214                            getOwner().mNotificationSwitch.setChecked(true);
1215                        }
1216                    })
1217                    .create();
1218                case DLG_SPECIAL_DISABLE:
1219                    return new AlertDialog.Builder(getActivity())
1220                    .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title))
1221                    .setIconAttribute(android.R.attr.alertDialogIcon)
1222                    .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text))
1223                    .setPositiveButton(R.string.dlg_ok,
1224                            new DialogInterface.OnClickListener() {
1225                        public void onClick(DialogInterface dialog, int which) {
1226                            // Clear user data here
1227                            getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1228                                    false, true);
1229                        }
1230                    })
1231                    .setNegativeButton(R.string.dlg_cancel, null)
1232                    .create();
1233            }
1234            throw new IllegalArgumentException("unknown id " + id);
1235        }
1236    }
1237
1238    private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
1239         // Create new intent to launch Uninstaller activity
1240        Uri packageURI = Uri.parse("package:"+packageName);
1241        Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
1242        uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
1243        startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
1244        mDisableAfterUninstall = andDisable;
1245    }
1246
1247    private void forceStopPackage(String pkgName) {
1248        ActivityManager am = (ActivityManager)getActivity().getSystemService(
1249                Context.ACTIVITY_SERVICE);
1250        am.forceStopPackage(pkgName);
1251        mState.invalidatePackage(pkgName);
1252        ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
1253        if (newEnt != null) {
1254            mAppEntry = newEnt;
1255        }
1256        checkForceStop();
1257    }
1258
1259    private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
1260        @Override
1261        public void onReceive(Context context, Intent intent) {
1262            updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
1263        }
1264    };
1265
1266    private void updateForceStopButton(boolean enabled) {
1267        mForceStopButton.setEnabled(enabled);
1268        mForceStopButton.setOnClickListener(InstalledAppDetails.this);
1269    }
1270
1271    private void checkForceStop() {
1272        if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
1273            // User can't force stop device admin.
1274            updateForceStopButton(false);
1275        } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
1276            // If the app isn't explicitly stopped, then always show the
1277            // force stop button.
1278            updateForceStopButton(true);
1279        } else {
1280            Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
1281                    Uri.fromParts("package", mAppEntry.info.packageName, null));
1282            intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
1283            intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
1284            intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
1285            getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
1286                    mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
1287        }
1288    }
1289
1290    static class DisableChanger extends AsyncTask<Object, Object, Object> {
1291        final PackageManager mPm;
1292        final WeakReference<InstalledAppDetails> mActivity;
1293        final ApplicationInfo mInfo;
1294        final int mState;
1295
1296        DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
1297            mPm = activity.mPm;
1298            mActivity = new WeakReference<InstalledAppDetails>(activity);
1299            mInfo = info;
1300            mState = state;
1301        }
1302
1303        @Override
1304        protected Object doInBackground(Object... params) {
1305            mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
1306            return null;
1307        }
1308    }
1309
1310    private void setNotificationsEnabled(boolean enabled) {
1311        String packageName = mAppEntry.info.packageName;
1312        INotificationManager nm = INotificationManager.Stub.asInterface(
1313                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1314        try {
1315            final boolean enable = mNotificationSwitch.isChecked();
1316            nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled);
1317        } catch (android.os.RemoteException ex) {
1318            mNotificationSwitch.setChecked(!enabled); // revert
1319        }
1320    }
1321
1322    private int getPremiumSmsPermission(String packageName) {
1323        try {
1324            if (mSmsManager != null) {
1325                return mSmsManager.getPremiumSmsPermission(packageName);
1326            }
1327        } catch (RemoteException ex) {
1328            // ignored
1329        }
1330        return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
1331    }
1332
1333    /*
1334     * Method implementing functionality of buttons clicked
1335     * @see android.view.View.OnClickListener#onClick(android.view.View)
1336     */
1337    public void onClick(View v) {
1338        String packageName = mAppEntry.info.packageName;
1339        if(v == mUninstallButton) {
1340            if (mUpdatedSysApp) {
1341                showDialogInner(DLG_FACTORY_RESET, 0);
1342            } else {
1343                if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1344                    if (mAppEntry.info.enabled) {
1345                        showDialogInner(DLG_DISABLE, 0);
1346                    } else {
1347                        new DisableChanger(this, mAppEntry.info,
1348                                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
1349                        .execute((Object)null);
1350                    }
1351                } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1352                    uninstallPkg(packageName, true, false);
1353                } else {
1354                    uninstallPkg(packageName, false, false);
1355                }
1356            }
1357        } else if(v == mSpecialDisableButton) {
1358            showDialogInner(DLG_SPECIAL_DISABLE, 0);
1359        } else if(v == mActivitiesButton) {
1360            mPm.clearPackagePreferredActivities(packageName);
1361            try {
1362                mUsbManager.clearDefaults(packageName, UserHandle.myUserId());
1363            } catch (RemoteException e) {
1364                Log.e(TAG, "mUsbManager.clearDefaults", e);
1365            }
1366            mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
1367            TextView autoLaunchTitleView =
1368                    (TextView) mRootView.findViewById(R.id.auto_launch_title);
1369            TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
1370            resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
1371        } else if(v == mClearDataButton) {
1372            if (mAppEntry.info.manageSpaceActivityName != null) {
1373                if (!Utils.isMonkeyRunning()) {
1374                    Intent intent = new Intent(Intent.ACTION_DEFAULT);
1375                    intent.setClassName(mAppEntry.info.packageName,
1376                            mAppEntry.info.manageSpaceActivityName);
1377                    startActivityForResult(intent, REQUEST_MANAGE_SPACE);
1378                }
1379            } else {
1380                showDialogInner(DLG_CLEAR_DATA, 0);
1381            }
1382        } else if (v == mClearCacheButton) {
1383            // Lazy initialization of observer
1384            if (mClearCacheObserver == null) {
1385                mClearCacheObserver = new ClearCacheObserver();
1386            }
1387            mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
1388        } else if (v == mForceStopButton) {
1389            showDialogInner(DLG_FORCE_STOP, 0);
1390            //forceStopPackage(mAppInfo.packageName);
1391        } else if (v == mMoveAppButton) {
1392            if (mPackageMoveObserver == null) {
1393                mPackageMoveObserver = new PackageMoveObserver();
1394            }
1395            int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
1396                    PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
1397            mMoveInProgress = true;
1398            refreshButtons();
1399            mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
1400        }
1401    }
1402
1403    @Override
1404    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1405        String packageName = mAppEntry.info.packageName;
1406        ActivityManager am = (ActivityManager)
1407                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1408        if (buttonView == mAskCompatibilityCB) {
1409            am.setPackageAskScreenCompat(packageName, isChecked);
1410        } else if (buttonView == mEnableCompatibilityCB) {
1411            am.setPackageScreenCompatMode(packageName, isChecked ?
1412                    ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
1413        } else if (buttonView == mNotificationSwitch) {
1414            if (!isChecked) {
1415                showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
1416            } else {
1417                setNotificationsEnabled(true);
1418            }
1419        }
1420    }
1421}
1422
1423