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