ManageApplications.java revision 07025891683aef83d850b0a91b37aa75b11ce33c
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.applications;
18
19import static android.net.NetworkPolicyManager.POLICY_NONE;
20import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
21
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.AlertDialog;
25import android.app.AppOpsManager;
26import android.app.Fragment;
27import android.app.INotificationManager;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.content.ServiceConnection;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
35import android.content.pm.PackageInfo;
36import android.content.pm.PackageManager;
37import android.net.NetworkPolicyManager;
38import android.net.Uri;
39import android.os.AsyncTask;
40import android.os.Bundle;
41import android.os.Environment;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.RemoteException;
45import android.os.ServiceManager;
46import android.os.UserHandle;
47import android.os.UserManager;
48import android.preference.PreferenceFrameLayout;
49import android.provider.Settings;
50import android.support.v4.view.PagerAdapter;
51import android.support.v4.view.PagerTabStrip;
52import android.support.v4.view.ViewPager;
53import android.util.Log;
54import android.view.LayoutInflater;
55import android.view.Menu;
56import android.view.MenuInflater;
57import android.view.MenuItem;
58import android.view.View;
59import android.view.ViewGroup;
60import android.view.animation.AnimationUtils;
61import android.widget.AbsListView;
62import android.widget.AdapterView;
63import android.widget.AdapterView.OnItemClickListener;
64import android.widget.AdapterView.OnItemSelectedListener;
65import android.widget.BaseAdapter;
66import android.widget.Filter;
67import android.widget.Filterable;
68import android.widget.ListView;
69import android.widget.Spinner;
70
71import com.android.internal.app.IMediaContainerService;
72import com.android.internal.content.PackageHelper;
73import com.android.settings.R;
74import com.android.settings.Settings.RunningServicesActivity;
75import com.android.settings.Settings.StorageUseActivity;
76import com.android.settings.UserSpinnerAdapter;
77import com.android.settings.Utils;
78import com.android.settings.applications.ApplicationsState.AppEntry;
79import com.android.settings.deviceinfo.StorageMeasurement;
80
81import java.util.ArrayList;
82import java.util.Comparator;
83import java.util.List;
84
85final class CanBeOnSdCardChecker {
86    final IPackageManager mPm;
87    int mInstallLocation;
88
89    CanBeOnSdCardChecker() {
90        mPm = IPackageManager.Stub.asInterface(
91                ServiceManager.getService("package"));
92    }
93
94    void init() {
95        try {
96            mInstallLocation = mPm.getInstallLocation();
97        } catch (RemoteException e) {
98            Log.e("CanBeOnSdCardChecker", "Is Package Manager running?");
99            return;
100        }
101    }
102
103    boolean check(ApplicationInfo info) {
104        boolean canBe = false;
105        if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
106            canBe = true;
107        } else {
108            if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
109                if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
110                        info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
111                    canBe = true;
112                } else if (info.installLocation
113                        == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
114                    if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) {
115                        // For apps with no preference and the default value set
116                        // to install on sdcard.
117                        canBe = true;
118                    }
119                }
120            }
121        }
122        return canBe;
123    }
124}
125
126interface AppClickListener {
127    void onItemClick(ManageApplications.TabInfo tab, AdapterView<?> parent,
128            View view, int position, long id);
129}
130
131/**
132 * Activity to pick an application that will be used to display installation information and
133 * options to uninstall/delete user data for system applications. This activity
134 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
135 * intent.
136 */
137public class ManageApplications extends Fragment implements
138        AppClickListener, DialogInterface.OnClickListener, DialogInterface.OnDismissListener  {
139
140    static final String TAG = "ManageApplications";
141    static final boolean DEBUG = false;
142
143    private static final String EXTRA_LIST_TYPE = "currentListType";
144    private static final String EXTRA_SORT_ORDER = "sortOrder";
145    private static final String EXTRA_SHOW_BACKGROUND = "showBackground";
146    private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType";
147    private static final String EXTRA_RESET_DIALOG = "resetDialog";
148
149    // attributes used as keys when passing values to InstalledAppDetails activity
150    public static final String APP_CHG = "chg";
151
152    // constant value that can be used to check return code from sub activity.
153    private static final int INSTALLED_APP_DETAILS = 1;
154
155    public static final int SIZE_TOTAL = 0;
156    public static final int SIZE_INTERNAL = 1;
157    public static final int SIZE_EXTERNAL = 2;
158
159    // sort order that can be changed through the menu can be sorted alphabetically
160    // or size(descending)
161    private static final int MENU_OPTIONS_BASE = 0;
162    // Filter options used for displayed list of applications
163    public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
164    public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
165    public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
166    public static final int FILTER_APPS_DISABLED = MENU_OPTIONS_BASE + 3;
167
168    public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
169    public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
170    public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
171    public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
172    public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8;
173    // sort order
174    private int mSortOrder = SORT_ORDER_ALPHA;
175
176    private ApplicationsState mApplicationsState;
177
178    public static class TabInfo implements OnItemClickListener {
179        public final ManageApplications mOwner;
180        public final ApplicationsState mApplicationsState;
181        public final CharSequence mLabel;
182        public final int mListType;
183        public final int mFilter;
184        public final AppClickListener mClickListener;
185        public final CharSequence mInvalidSizeStr;
186        public final CharSequence mComputingSizeStr;
187        private final Bundle mSavedInstanceState;
188
189        public ApplicationsAdapter mApplications;
190        public LayoutInflater mInflater;
191        public View mRootView;
192
193        private IMediaContainerService mContainerService;
194
195        private View mLoadingContainer;
196
197        private View mListContainer;
198
199        // ListView used to display list
200        private ListView mListView;
201        // Custom view used to display running processes
202        private RunningProcessesView mRunningProcessesView;
203
204        //private LinearColorBar mColorBar;
205        //private TextView mStorageChartLabel;
206        //private TextView mUsedStorageText;
207        //private TextView mFreeStorageText;
208        private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0;
209        private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
210
211        final Runnable mRunningProcessesAvail = new Runnable() {
212            public void run() {
213                handleRunningProcessesAvail();
214            }
215        };
216
217        public TabInfo(ManageApplications owner, ApplicationsState apps,
218                CharSequence label, int listType, AppClickListener clickListener,
219                Bundle savedInstanceState) {
220            mOwner = owner;
221            mApplicationsState = apps;
222            mLabel = label;
223            mListType = listType;
224            switch (listType) {
225                case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break;
226                case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break;
227                case LIST_TYPE_DISABLED: mFilter = FILTER_APPS_DISABLED; break;
228                default: mFilter = FILTER_APPS_ALL; break;
229            }
230            mClickListener = clickListener;
231            mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value);
232            mComputingSizeStr = owner.getActivity().getText(R.string.computing_size);
233            mSavedInstanceState = savedInstanceState;
234        }
235
236        public void setContainerService(IMediaContainerService containerService) {
237            mContainerService = containerService;
238            updateStorageUsage();
239        }
240
241        public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) {
242            if (mRootView != null) {
243                return mRootView;
244            }
245            mInflater = inflater;
246            mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING
247                    ? R.layout.manage_applications_running
248                    : R.layout.manage_applications_apps, null);
249            mLoadingContainer = mRootView.findViewById(R.id.loading_container);
250            mLoadingContainer.setVisibility(View.VISIBLE);
251            mListContainer = mRootView.findViewById(R.id.list_container);
252            if (mListContainer != null) {
253                // Create adapter and list view here
254                View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
255                ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
256                if (emptyView != null) {
257                    lv.setEmptyView(emptyView);
258                }
259                lv.setOnItemClickListener(this);
260                lv.setSaveEnabled(true);
261                lv.setItemsCanFocus(true);
262                lv.setTextFilterEnabled(true);
263                mListView = lv;
264                mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
265                mListView.setAdapter(mApplications);
266                mListView.setRecyclerListener(mApplications);
267                //mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
268                //mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
269                //mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
270                //mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
271                Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false);
272                if (mFilter == FILTER_APPS_SDCARD) {
273                    //mStorageChartLabel.setText(mOwner.getActivity().getText(
274                    //        R.string.sd_card_storage));
275                } else {
276                    //mStorageChartLabel.setText(mOwner.getActivity().getText(
277                    //        R.string.internal_storage));
278                }
279                applyCurrentStorage();
280            }
281            mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
282                    R.id.running_processes);
283            if (mRunningProcessesView != null) {
284                mRunningProcessesView.doCreate(mSavedInstanceState);
285            }
286
287            return mRootView;
288        }
289
290        public void detachView() {
291            if (mRootView != null) {
292                ViewGroup group = (ViewGroup)mRootView.getParent();
293                if (group != null) {
294                    group.removeView(mRootView);
295                }
296            }
297        }
298
299        public void resume(int sortOrder) {
300            if (mApplications != null) {
301                mApplications.resume(sortOrder);
302            }
303            if (mRunningProcessesView != null) {
304                boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail);
305                if (haveData) {
306                    mRunningProcessesView.setVisibility(View.VISIBLE);
307                    mLoadingContainer.setVisibility(View.INVISIBLE);
308                } else {
309                    mLoadingContainer.setVisibility(View.VISIBLE);
310                }
311            }
312        }
313
314        public void pause() {
315            if (mApplications != null) {
316                mApplications.pause();
317            }
318            if (mRunningProcessesView != null) {
319                mRunningProcessesView.doPause();
320            }
321        }
322
323        public void release() {
324            if (mApplications != null) {
325                mApplications.release();
326            }
327        }
328
329        void updateStorageUsage() {
330            // Make sure a callback didn't come at an inopportune time.
331            if (mOwner.getActivity() == null) return;
332            // Doesn't make sense for stuff that is not an app list.
333            if (mApplications == null) return;
334
335            mFreeStorage = 0;
336            mAppStorage = 0;
337            mTotalStorage = 0;
338
339            if (mFilter == FILTER_APPS_SDCARD) {
340                if (mContainerService != null) {
341                    try {
342                        final long[] stats = mContainerService.getFileSystemStats(
343                                Environment.getExternalStorageDirectory().getPath());
344                        mTotalStorage = stats[0];
345                        mFreeStorage = stats[1];
346                    } catch (RemoteException e) {
347                        Log.w(TAG, "Problem in container service", e);
348                    }
349                }
350
351                if (mApplications != null) {
352                    final int N = mApplications.getCount();
353                    for (int i=0; i<N; i++) {
354                        ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
355                        mAppStorage += ae.externalCodeSize + ae.externalDataSize
356                                + ae.externalCacheSize;
357                    }
358                }
359            } else {
360                if (mContainerService != null) {
361                    try {
362                        final long[] stats = mContainerService.getFileSystemStats(
363                                Environment.getDataDirectory().getPath());
364                        mTotalStorage = stats[0];
365                        mFreeStorage = stats[1];
366                    } catch (RemoteException e) {
367                        Log.w(TAG, "Problem in container service", e);
368                    }
369                }
370
371                final boolean emulatedStorage = Environment.isExternalStorageEmulated();
372                if (mApplications != null) {
373                    final int N = mApplications.getCount();
374                    for (int i=0; i<N; i++) {
375                        ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
376                        mAppStorage += ae.codeSize + ae.dataSize;
377                        if (emulatedStorage) {
378                            mAppStorage += ae.externalCodeSize + ae.externalDataSize;
379                        }
380                    }
381                }
382                mFreeStorage += mApplicationsState.sumCacheSizes();
383            }
384
385            applyCurrentStorage();
386        }
387
388        void applyCurrentStorage() {
389            // If view hierarchy is not yet created, no views to update.
390            if (mRootView == null) {
391                return;
392            }
393            /*
394            if (mTotalStorage > 0) {
395                BidiFormatter bidiFormatter = BidiFormatter.getInstance();
396                mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage,
397                        mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage);
398                long usedStorage = mTotalStorage - mFreeStorage;
399                if (mLastUsedStorage != usedStorage) {
400                    mLastUsedStorage = usedStorage;
401                    String sizeStr = bidiFormatter.unicodeWrap(
402                            Formatter.formatShortFileSize(mOwner.getActivity(), usedStorage));
403                    mUsedStorageText.setText(mOwner.getActivity().getResources().getString(
404                            R.string.service_foreground_processes, sizeStr));
405                }
406                if (mLastFreeStorage != mFreeStorage) {
407                    mLastFreeStorage = mFreeStorage;
408                    String sizeStr = bidiFormatter.unicodeWrap(
409                            Formatter.formatShortFileSize(mOwner.getActivity(), mFreeStorage));
410                    mFreeStorageText.setText(mOwner.getActivity().getResources().getString(
411                            R.string.service_background_processes, sizeStr));
412                }
413            } else {
414                mColorBar.setRatios(0, 0, 0);
415                if (mLastUsedStorage != -1) {
416                    mLastUsedStorage = -1;
417                    mUsedStorageText.setText("");
418                }
419                if (mLastFreeStorage != -1) {
420                    mLastFreeStorage = -1;
421                    mFreeStorageText.setText("");
422                }
423            }
424            */
425        }
426
427        @Override
428        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
429            mClickListener.onItemClick(this, parent, view, position, id);
430        }
431
432        void handleRunningProcessesAvail() {
433            mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
434                    mOwner.getActivity(), android.R.anim.fade_out));
435            mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
436                    mOwner.getActivity(), android.R.anim.fade_in));
437            mRunningProcessesView.setVisibility(View.VISIBLE);
438            mLoadingContainer.setVisibility(View.GONE);
439        }
440    }
441    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
442    private int mNumTabs;
443    TabInfo mCurTab = null;
444
445    // Size resource used for packages whose size computation failed for some reason
446    CharSequence mInvalidSizeStr;
447    private CharSequence mComputingSizeStr;
448
449    // layout inflater object used to inflate views
450    private LayoutInflater mInflater;
451
452    private String mCurrentPkgName;
453    private int mCurrentUid;
454
455    private Menu mOptionsMenu;
456
457    // These are for keeping track of activity and spinner switch state.
458    private boolean mActivityResumed;
459
460    private static final int LIST_TYPE_MISSING = -1;
461    static final int LIST_TYPE_DOWNLOADED = 0;
462    static final int LIST_TYPE_RUNNING = 1;
463    static final int LIST_TYPE_SDCARD = 2;
464    static final int LIST_TYPE_ALL = 3;
465    static final int LIST_TYPE_DISABLED = 4;
466
467    private boolean mShowBackground = false;
468
469    private int mDefaultListType = -1;
470
471    private ViewGroup mContentContainer;
472    private View mRootView;
473    private ViewPager mViewPager;
474    private ViewGroup mPinnedHeader;
475    private Spinner mSpinner;
476    private Context mContext;
477
478    AlertDialog mResetDialog;
479
480    class MyPagerAdapter extends PagerAdapter
481            implements ViewPager.OnPageChangeListener {
482        int mCurPos = 0;
483
484        @Override
485        public int getCount() {
486            return mNumTabs;
487        }
488
489        @Override
490        public Object instantiateItem(ViewGroup container, int position) {
491            TabInfo tab = mTabs.get(position);
492            View root = tab.build(mInflater, mContentContainer, mRootView);
493            container.addView(root);
494            root.setTag(R.id.name, tab);
495            return root;
496        }
497
498        @Override
499        public void destroyItem(ViewGroup container, int position, Object object) {
500            container.removeView((View)object);
501        }
502
503        @Override
504        public boolean isViewFromObject(View view, Object object) {
505            return view == object;
506        }
507
508        @Override
509        public int getItemPosition(Object object) {
510            return super.getItemPosition(object);
511            //return ((TabInfo)((View)object).getTag(R.id.name)).mListType;
512        }
513
514        @Override
515        public CharSequence getPageTitle(int position) {
516            return mTabs.get(position).mLabel;
517        }
518
519        @Override
520        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
521        }
522
523        @Override
524        public void onPageSelected(int position) {
525            mCurPos = position;
526        }
527
528        @Override
529        public void onPageScrollStateChanged(int state) {
530            if (state == ViewPager.SCROLL_STATE_IDLE) {
531                updateCurrentTab(mCurPos);
532            }
533        }
534    }
535
536    /*
537     * Custom adapter implementation for the ListView
538     * This adapter maintains a map for each displayed application and its properties
539     * An index value on each AppInfo object indicates the correct position or index
540     * in the list. If the list gets updated dynamically when the user is viewing the list of
541     * applications, we need to return the correct index of position. This is done by mapping
542     * the getId methods via the package name into the internal maps and indices.
543     * The order of applications in the list is mirrored in mAppLocalList
544     */
545    static class ApplicationsAdapter extends BaseAdapter implements Filterable,
546            ApplicationsState.Callbacks, AbsListView.RecyclerListener {
547        private final ApplicationsState mState;
548        private final ApplicationsState.Session mSession;
549        private final TabInfo mTab;
550        private final Context mContext;
551        private final ArrayList<View> mActive = new ArrayList<View>();
552        private final int mFilterMode;
553        private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
554        private ArrayList<ApplicationsState.AppEntry> mEntries;
555        private boolean mResumed;
556        private int mLastSortMode=-1;
557        private boolean mWaitingForData;
558        private int mWhichSize = SIZE_TOTAL;
559        CharSequence mCurFilterPrefix;
560
561        private Filter mFilter = new Filter() {
562            @Override
563            protected FilterResults performFiltering(CharSequence constraint) {
564                ArrayList<ApplicationsState.AppEntry> entries
565                        = applyPrefixFilter(constraint, mBaseEntries);
566                FilterResults fr = new FilterResults();
567                fr.values = entries;
568                fr.count = entries.size();
569                return fr;
570            }
571
572            @Override
573            protected void publishResults(CharSequence constraint, FilterResults results) {
574                mCurFilterPrefix = constraint;
575                mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
576                notifyDataSetChanged();
577                mTab.updateStorageUsage();
578            }
579        };
580
581        public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) {
582            mState = state;
583            mSession = state.newSession(this);
584            mTab = tab;
585            mContext = tab.mOwner.getActivity();
586            mFilterMode = filterMode;
587        }
588
589        public void resume(int sort) {
590            if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
591            if (!mResumed) {
592                mResumed = true;
593                mSession.resume();
594                mLastSortMode = sort;
595                rebuild(true);
596            } else {
597                rebuild(sort);
598            }
599        }
600
601        public void pause() {
602            if (mResumed) {
603                mResumed = false;
604                mSession.pause();
605            }
606        }
607
608        public void release() {
609            mSession.release();
610        }
611
612        public void rebuild(int sort) {
613            if (sort == mLastSortMode) {
614                return;
615            }
616            mLastSortMode = sort;
617            rebuild(true);
618        }
619
620        public void rebuild(boolean eraseold) {
621            if (DEBUG) Log.i(TAG, "Rebuilding app list...");
622            ApplicationsState.AppFilter filterObj;
623            Comparator<AppEntry> comparatorObj;
624            boolean emulated = Environment.isExternalStorageEmulated();
625            if (emulated) {
626                mWhichSize = SIZE_TOTAL;
627            } else {
628                mWhichSize = SIZE_INTERNAL;
629            }
630            switch (mFilterMode) {
631                case FILTER_APPS_THIRD_PARTY:
632                    filterObj = ApplicationsState.THIRD_PARTY_FILTER;
633                    break;
634                case FILTER_APPS_SDCARD:
635                    filterObj = ApplicationsState.ON_SD_CARD_FILTER;
636                    if (!emulated) {
637                        mWhichSize = SIZE_EXTERNAL;
638                    }
639                    break;
640                case FILTER_APPS_DISABLED:
641                    filterObj = ApplicationsState.DISABLED_FILTER;
642                    break;
643                default:
644                    filterObj = ApplicationsState.ALL_ENABLED_FILTER;
645                    break;
646            }
647            switch (mLastSortMode) {
648                case SORT_ORDER_SIZE:
649                    switch (mWhichSize) {
650                        case SIZE_INTERNAL:
651                            comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
652                            break;
653                        case SIZE_EXTERNAL:
654                            comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
655                            break;
656                        default:
657                            comparatorObj = ApplicationsState.SIZE_COMPARATOR;
658                            break;
659                    }
660                    break;
661                default:
662                    comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
663                    break;
664            }
665            ArrayList<ApplicationsState.AppEntry> entries
666                    = mSession.rebuild(filterObj, comparatorObj);
667            if (entries == null && !eraseold) {
668                // Don't have new list yet, but can continue using the old one.
669                return;
670            }
671            mBaseEntries = entries;
672            if (mBaseEntries != null) {
673                mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
674            } else {
675                mEntries = null;
676            }
677            notifyDataSetChanged();
678            mTab.updateStorageUsage();
679
680            if (entries == null) {
681                mWaitingForData = true;
682                mTab.mListContainer.setVisibility(View.INVISIBLE);
683                mTab.mLoadingContainer.setVisibility(View.VISIBLE);
684            } else {
685                mTab.mListContainer.setVisibility(View.VISIBLE);
686                mTab.mLoadingContainer.setVisibility(View.GONE);
687            }
688        }
689
690        ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
691                ArrayList<ApplicationsState.AppEntry> origEntries) {
692            if (prefix == null || prefix.length() == 0) {
693                return origEntries;
694            } else {
695                String prefixStr = ApplicationsState.normalize(prefix.toString());
696                final String spacePrefixStr = " " + prefixStr;
697                ArrayList<ApplicationsState.AppEntry> newEntries
698                        = new ArrayList<ApplicationsState.AppEntry>();
699                for (int i=0; i<origEntries.size(); i++) {
700                    ApplicationsState.AppEntry entry = origEntries.get(i);
701                    String nlabel = entry.getNormalizedLabel();
702                    if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
703                        newEntries.add(entry);
704                    }
705                }
706                return newEntries;
707            }
708        }
709
710        @Override
711        public void onRunningStateChanged(boolean running) {
712            mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running);
713        }
714
715        @Override
716        public void onRebuildComplete(ArrayList<AppEntry> apps) {
717            if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) {
718                mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
719                        mContext, android.R.anim.fade_out));
720                mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation(
721                        mContext, android.R.anim.fade_in));
722            }
723            mTab.mListContainer.setVisibility(View.VISIBLE);
724            mTab.mLoadingContainer.setVisibility(View.GONE);
725            mWaitingForData = false;
726            mBaseEntries = apps;
727            mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
728            notifyDataSetChanged();
729            mTab.updateStorageUsage();
730        }
731
732        @Override
733        public void onPackageListChanged() {
734            rebuild(false);
735        }
736
737        @Override
738        public void onPackageIconChanged() {
739            // We ensure icons are loaded when their item is displayed, so
740            // don't care about icons loaded in the background.
741        }
742
743        @Override
744        public void onPackageSizeChanged(String packageName) {
745            for (int i=0; i<mActive.size(); i++) {
746                AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
747                if (holder.entry.info.packageName.equals(packageName)) {
748                    synchronized (holder.entry) {
749                        holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
750                    }
751                    if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName)
752                            && mLastSortMode == SORT_ORDER_SIZE) {
753                        // We got the size information for the last app the
754                        // user viewed, and are sorting by size...  they may
755                        // have cleared data, so we immediately want to resort
756                        // the list with the new size to reflect it to the user.
757                        rebuild(false);
758                    }
759                    mTab.updateStorageUsage();
760                    return;
761                }
762            }
763        }
764
765        @Override
766        public void onAllSizesComputed() {
767            if (mLastSortMode == SORT_ORDER_SIZE) {
768                rebuild(false);
769            }
770            mTab.updateStorageUsage();
771        }
772
773        public int getCount() {
774            return mEntries != null ? mEntries.size() : 0;
775        }
776
777        public Object getItem(int position) {
778            return mEntries.get(position);
779        }
780
781        public ApplicationsState.AppEntry getAppEntry(int position) {
782            return mEntries.get(position);
783        }
784
785        public long getItemId(int position) {
786            return mEntries.get(position).id;
787        }
788
789        public View getView(int position, View convertView, ViewGroup parent) {
790            // A ViewHolder keeps references to children views to avoid unnecessary calls
791            // to findViewById() on each row.
792            AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView);
793            convertView = holder.rootView;
794
795            // Bind the data efficiently with the holder
796            ApplicationsState.AppEntry entry = mEntries.get(position);
797            synchronized (entry) {
798                holder.entry = entry;
799                if (entry.label != null) {
800                    holder.appName.setText(entry.label);
801                }
802                mState.ensureIcon(entry);
803                if (entry.icon != null) {
804                    holder.appIcon.setImageDrawable(entry.icon);
805                }
806                holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
807                if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
808                    holder.disabled.setVisibility(View.VISIBLE);
809                    holder.disabled.setText(R.string.not_installed);
810                } else if (!entry.info.enabled) {
811                    holder.disabled.setVisibility(View.VISIBLE);
812                    holder.disabled.setText(R.string.disabled);
813                } else {
814                    holder.disabled.setVisibility(View.GONE);
815                }
816                if (mFilterMode == FILTER_APPS_SDCARD) {
817                    holder.checkBox.setVisibility(View.VISIBLE);
818                    holder.checkBox.setChecked((entry.info.flags
819                            & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
820                } else {
821                    holder.checkBox.setVisibility(View.GONE);
822                }
823            }
824            mActive.remove(convertView);
825            mActive.add(convertView);
826            return convertView;
827        }
828
829        @Override
830        public Filter getFilter() {
831            return mFilter;
832        }
833
834        @Override
835        public void onMovedToScrapHeap(View view) {
836            mActive.remove(view);
837        }
838    }
839
840    @Override
841    public void onCreate(Bundle savedInstanceState) {
842        super.onCreate(savedInstanceState);
843
844        setHasOptionsMenu(true);
845
846        mContext = getActivity();
847        mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
848        Intent intent = getActivity().getIntent();
849        String action = intent.getAction();
850        int defaultListType = LIST_TYPE_DOWNLOADED;
851        String className = getArguments() != null
852                ? getArguments().getString("classname") : null;
853        if (className == null) {
854            className = intent.getComponent().getClassName();
855        }
856        if (className.equals(RunningServicesActivity.class.getName())
857                || className.endsWith(".RunningServices")) {
858            defaultListType = LIST_TYPE_RUNNING;
859        } else if (className.equals(StorageUseActivity.class.getName())
860                || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)
861                || className.endsWith(".StorageUse")) {
862            mSortOrder = SORT_ORDER_SIZE;
863            defaultListType = LIST_TYPE_ALL;
864        } else if (android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {
865            // Select the all-apps list, with the default sorting
866            defaultListType = LIST_TYPE_ALL;
867        }
868
869        if (savedInstanceState != null) {
870            mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
871            int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1);
872            if (tmp != -1) defaultListType = tmp;
873            mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false);
874        }
875
876        mDefaultListType = defaultListType;
877
878        final Intent containerIntent = new Intent().setComponent(
879                StorageMeasurement.DEFAULT_CONTAINER_COMPONENT);
880        getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);
881
882        mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
883        mComputingSizeStr = getActivity().getText(R.string.computing_size);
884
885        TabInfo tab = new TabInfo(this, mApplicationsState,
886                getActivity().getString(R.string.filter_apps_third_party),
887                LIST_TYPE_DOWNLOADED, this, savedInstanceState);
888        mTabs.add(tab);
889
890        if (!Environment.isExternalStorageEmulated()) {
891            tab = new TabInfo(this, mApplicationsState,
892                    getActivity().getString(R.string.filter_apps_onsdcard),
893                    LIST_TYPE_SDCARD, this, savedInstanceState);
894            mTabs.add(tab);
895        }
896
897        tab = new TabInfo(this, mApplicationsState,
898                getActivity().getString(R.string.filter_apps_running),
899                LIST_TYPE_RUNNING, this, savedInstanceState);
900        mTabs.add(tab);
901
902        tab = new TabInfo(this, mApplicationsState,
903                getActivity().getString(R.string.filter_apps_all),
904                LIST_TYPE_ALL, this, savedInstanceState);
905        mTabs.add(tab);
906
907        tab = new TabInfo(this, mApplicationsState,
908                getActivity().getString(R.string.filter_apps_disabled),
909                LIST_TYPE_DISABLED, this, savedInstanceState);
910        mTabs.add(tab);
911
912        mNumTabs = mTabs.size();
913    }
914
915
916    @Override
917    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
918        // initialize the inflater
919        mInflater = inflater;
920
921        View rootView = mInflater.inflate(R.layout.manage_applications_content,
922                container, false);
923        mContentContainer = container;
924        mRootView = rootView;
925        mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
926        MyPagerAdapter adapter = new MyPagerAdapter();
927        mViewPager.setAdapter(adapter);
928        mViewPager.setOnPageChangeListener(adapter);
929        PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
930        tabs.setTabIndicatorColorResource(R.color.theme_accent);
931
932        // We have to do this now because PreferenceFrameLayout looks at it
933        // only when the view is added.
934        if (container instanceof PreferenceFrameLayout) {
935            ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
936        }
937
938        if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {
939            buildResetDialog();
940        }
941
942        if (savedInstanceState == null) {
943            // First time init: make sure view pager is showing the correct tab.
944            int extraCurrentListType = getActivity().getIntent().getIntExtra(EXTRA_LIST_TYPE,
945                    LIST_TYPE_MISSING);
946            int currentListType = (extraCurrentListType != LIST_TYPE_MISSING)
947                    ? extraCurrentListType : mDefaultListType;
948            for (int i = 0; i < mNumTabs; i++) {
949                TabInfo tab = mTabs.get(i);
950                if (tab.mListType == currentListType) {
951                    mViewPager.setCurrentItem(i);
952                    break;
953                }
954            }
955        }
956
957        return rootView;
958    }
959
960    @Override
961    public void onStart() {
962        super.onStart();
963    }
964
965    @Override
966    public void onResume() {
967        super.onResume();
968        mActivityResumed = true;
969        updateCurrentTab(mViewPager.getCurrentItem());
970        updateNumTabs();
971        updateOptionsMenu();
972    }
973
974    @Override
975    public void onSaveInstanceState(Bundle outState) {
976        super.onSaveInstanceState(outState);
977        outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
978        if (mDefaultListType != -1) {
979            outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType);
980        }
981        outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground);
982        if (mResetDialog != null) {
983            outState.putBoolean(EXTRA_RESET_DIALOG, true);
984        }
985    }
986
987    @Override
988    public void onPause() {
989        super.onPause();
990        mActivityResumed = false;
991        for (int i=0; i<mTabs.size(); i++) {
992            mTabs.get(i).pause();
993        }
994    }
995
996    @Override
997    public void onStop() {
998        super.onStop();
999        if (mResetDialog != null) {
1000            mResetDialog.dismiss();
1001            mResetDialog = null;
1002        }
1003    }
1004
1005    @Override
1006    public void onDestroyView() {
1007        super.onDestroyView();
1008
1009        // We are going to keep the tab data structures around, but they
1010        // are no longer attached to their view hierarchy.
1011        for (int i=0; i<mTabs.size(); i++) {
1012            mTabs.get(i).detachView();
1013            mTabs.get(i).release();
1014        }
1015    }
1016
1017    @Override
1018    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1019        if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
1020            mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
1021        }
1022    }
1023
1024    private void updateNumTabs() {
1025        int newNum = mApplicationsState.haveDisabledApps() ? mTabs.size() : (mTabs.size()-1);
1026        if (newNum != mNumTabs) {
1027            mNumTabs = newNum;
1028            if (mViewPager != null) {
1029                mViewPager.getAdapter().notifyDataSetChanged();
1030            }
1031        }
1032    }
1033
1034    TabInfo tabForType(int type) {
1035        for (int i = 0; i < mTabs.size(); i++) {
1036            TabInfo tab = mTabs.get(i);
1037            if (tab.mListType == type) {
1038                return tab;
1039            }
1040        }
1041        return null;
1042    }
1043
1044    // utility method used to start sub activity
1045    private void startApplicationDetailsActivity() {
1046        // TODO: Figure out if there is a way where we can spin up the profile's settings
1047        // process ahead of time, to avoid a long load of data when user clicks on a managed app.
1048        // Maybe when they load the list of apps that contains managed profile apps.
1049        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
1050        intent.setData(Uri.fromParts("package", mCurrentPkgName, null));
1051        getActivity().startActivityAsUser(intent,
1052                new UserHandle(UserHandle.getUserId(mCurrentUid)));
1053    }
1054
1055    @Override
1056    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1057        mOptionsMenu = menu;
1058        // note: icons removed for now because the cause the new action
1059        // bar UI to be very confusing.
1060        menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
1061                //.setIcon(android.R.drawable.ic_menu_sort_alphabetically)
1062                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1063        menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1064                //.setIcon(android.R.drawable.ic_menu_sort_by_size)
1065                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1066        menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services)
1067                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
1068        menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes)
1069                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
1070        menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences)
1071                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1072        updateOptionsMenu();
1073    }
1074
1075    @Override
1076    public void onPrepareOptionsMenu(Menu menu) {
1077        updateOptionsMenu();
1078    }
1079
1080    @Override
1081    public void onDestroyOptionsMenu() {
1082        mOptionsMenu = null;
1083    }
1084
1085    @Override
1086    public void onDestroy() {
1087        getActivity().unbindService(mContainerConnection);
1088        super.onDestroy();
1089    }
1090
1091    void updateOptionsMenu() {
1092        if (mOptionsMenu == null) {
1093            return;
1094        }
1095
1096        /*
1097         * The running processes screen doesn't use the mApplicationsAdapter
1098         * so bringing up this menu in that case doesn't make any sense.
1099         */
1100        if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) {
1101            TabInfo tab = tabForType(LIST_TYPE_RUNNING);
1102            boolean showingBackground = tab != null && tab.mRunningProcessesView != null
1103                    ? tab.mRunningProcessesView.mAdapter.getShowBackground() : false;
1104            mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false);
1105            mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false);
1106            mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
1107            mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
1108            mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false);
1109            mShowBackground = showingBackground;
1110        } else {
1111            mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
1112            mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
1113            mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
1114            mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
1115            mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true);
1116        }
1117    }
1118
1119    void buildResetDialog() {
1120        if (mResetDialog == null) {
1121            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
1122            builder.setTitle(R.string.reset_app_preferences_title);
1123            builder.setMessage(R.string.reset_app_preferences_desc);
1124            builder.setPositiveButton(R.string.reset_app_preferences_button, this);
1125            builder.setNegativeButton(R.string.cancel, null);
1126            mResetDialog = builder.show();
1127            mResetDialog.setOnDismissListener(this);
1128        }
1129    }
1130
1131    @Override
1132    public void onDismiss(DialogInterface dialog) {
1133        if (mResetDialog == dialog) {
1134            mResetDialog = null;
1135        }
1136    }
1137
1138
1139    @Override
1140    public void onClick(DialogInterface dialog, int which) {
1141        if (mResetDialog == dialog) {
1142            final PackageManager pm = getActivity().getPackageManager();
1143            final IPackageManager mIPm = IPackageManager.Stub.asInterface(
1144                    ServiceManager.getService("package"));
1145            final INotificationManager nm = INotificationManager.Stub.asInterface(
1146                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1147            final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity());
1148            final AppOpsManager aom = (AppOpsManager)getActivity().getSystemService(
1149                    Context.APP_OPS_SERVICE);
1150            final Handler handler = new Handler(getActivity().getMainLooper());
1151            (new AsyncTask<Void, Void, Void>() {
1152                @Override protected Void doInBackground(Void... params) {
1153                    List<ApplicationInfo> apps = pm.getInstalledApplications(
1154                            PackageManager.GET_DISABLED_COMPONENTS);
1155                    for (int i=0; i<apps.size(); i++) {
1156                        ApplicationInfo app = apps.get(i);
1157                        try {
1158                            if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName);
1159                            nm.setNotificationsEnabledForPackage(app.packageName, app.uid, true);
1160                        } catch (android.os.RemoteException ex) {
1161                        }
1162                        if (!app.enabled) {
1163                            if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName);
1164                            if (pm.getApplicationEnabledSetting(app.packageName)
1165                                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
1166                                pm.setApplicationEnabledSetting(app.packageName,
1167                                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
1168                                        PackageManager.DONT_KILL_APP);
1169                            }
1170                        }
1171                    }
1172                    try {
1173                        mIPm.resetPreferredActivities(UserHandle.myUserId());
1174                    } catch (RemoteException e) {
1175                    }
1176                    aom.resetAllModes();
1177                    final int[] restrictedUids = npm.getUidsWithPolicy(
1178                            POLICY_REJECT_METERED_BACKGROUND);
1179                    final int currentUserId = ActivityManager.getCurrentUser();
1180                    for (int uid : restrictedUids) {
1181                        // Only reset for current user
1182                        if (UserHandle.getUserId(uid) == currentUserId) {
1183                            if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid);
1184                            npm.setUidPolicy(uid, POLICY_NONE);
1185                        }
1186                    }
1187                    handler.post(new Runnable() {
1188                        @Override public void run() {
1189                            if (DEBUG) Log.v(TAG, "Done clearing");
1190                            if (getActivity() != null && mActivityResumed) {
1191                                if (DEBUG) Log.v(TAG, "Updating UI!");
1192                                for (int i=0; i<mTabs.size(); i++) {
1193                                    TabInfo tab = mTabs.get(i);
1194                                    if (tab.mApplications != null) {
1195                                        tab.mApplications.pause();
1196                                    }
1197                                }
1198                                if (mCurTab != null) {
1199                                    mCurTab.resume(mSortOrder);
1200                                }
1201                            }
1202                        }
1203                    });
1204                    return null;
1205                }
1206            }).execute();
1207        }
1208    }
1209
1210    @Override
1211    public boolean onOptionsItemSelected(MenuItem item) {
1212        int menuId = item.getItemId();
1213        if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
1214            mSortOrder = menuId;
1215            if (mCurTab != null && mCurTab.mApplications != null) {
1216                mCurTab.mApplications.rebuild(mSortOrder);
1217            }
1218        } else if (menuId == SHOW_RUNNING_SERVICES) {
1219            mShowBackground = false;
1220            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
1221                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false);
1222            }
1223        } else if (menuId == SHOW_BACKGROUND_PROCESSES) {
1224            mShowBackground = true;
1225            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
1226                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true);
1227            }
1228        } else if (menuId == RESET_APP_PREFERENCES) {
1229            buildResetDialog();
1230        } else {
1231            // Handle the home button
1232            return false;
1233        }
1234        updateOptionsMenu();
1235        return true;
1236    }
1237
1238    public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position,
1239            long id) {
1240        if (tab.mApplications != null && tab.mApplications.getCount() > position) {
1241            ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position);
1242            mCurrentPkgName = entry.info.packageName;
1243            mCurrentUid = entry.info.uid;
1244            startApplicationDetailsActivity();
1245        }
1246    }
1247
1248    public void updateCurrentTab(int position) {
1249        TabInfo tab = mTabs.get(position);
1250        mCurTab = tab;
1251
1252        // Put things in the correct paused/resumed state.
1253        if (mActivityResumed) {
1254            mCurTab.build(mInflater, mContentContainer, mRootView);
1255            mCurTab.resume(mSortOrder);
1256        } else {
1257            mCurTab.pause();
1258        }
1259        for (int i=0; i<mTabs.size(); i++) {
1260            TabInfo t = mTabs.get(i);
1261            if (t != mCurTab) {
1262                t.pause();
1263            }
1264        }
1265
1266        mCurTab.updateStorageUsage();
1267        updateOptionsMenu();
1268        final Activity host = getActivity();
1269        if (host != null) {
1270            host.invalidateOptionsMenu();
1271        }
1272    }
1273
1274    private volatile IMediaContainerService mContainerService;
1275
1276    private final ServiceConnection mContainerConnection = new ServiceConnection() {
1277        @Override
1278        public void onServiceConnected(ComponentName name, IBinder service) {
1279            mContainerService = IMediaContainerService.Stub.asInterface(service);
1280            for (int i=0; i<mTabs.size(); i++) {
1281                mTabs.get(i).setContainerService(mContainerService);
1282            }
1283        }
1284
1285        @Override
1286        public void onServiceDisconnected(ComponentName name) {
1287            mContainerService = null;
1288        }
1289    };
1290}
1291