RunningServiceDetails.java revision c01b0c83fca571229621d16b757a46dc0fae7dfe
1package com.android.settings.applications;
2
3import com.android.settings.R;
4
5import android.app.Activity;
6import android.app.ActivityManager;
7import android.app.PendingIntent;
8import android.content.ActivityNotFoundException;
9import android.content.ComponentName;
10import android.content.Context;
11import android.content.Intent;
12import android.content.IntentSender;
13import android.content.pm.PackageManager;
14import android.content.pm.ProviderInfo;
15import android.content.pm.ServiceInfo;
16import android.content.pm.PackageManager.NameNotFoundException;
17import android.content.res.Resources;
18import android.os.Bundle;
19import android.os.Handler;
20import android.os.HandlerThread;
21import android.os.Looper;
22import android.os.Message;
23import android.util.Log;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.LinearLayout;
28import android.widget.TextView;
29
30import java.util.ArrayList;
31import java.util.List;
32
33public class RunningServiceDetails extends Activity {
34    static final String TAG = "RunningServicesDetails";
35
36    static final String KEY_UID = "uid";
37    static final String KEY_PROCESS = "process";
38
39    static final int MSG_UPDATE_TIMES = 1;
40    static final int MSG_UPDATE_CONTENTS = 2;
41    static final int MSG_REFRESH_UI = 3;
42
43    ActivityManager mAm;
44    LayoutInflater mInflater;
45
46    RunningState mState;
47
48    int mUid;
49    String mProcessName;
50
51    RunningState.MergedItem mMergedItem;
52
53    ViewGroup mAllDetails;
54    ViewGroup mSnippet;
55    RunningProcessesView.ActiveItem mSnippetActiveItem;
56    RunningProcessesView.ViewHolder mSnippetViewHolder;
57
58    int mNumServices, mNumProcesses;
59
60    TextView mServicesHeader;
61    TextView mProcessesHeader;
62    final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
63
64    class ActiveDetail implements View.OnClickListener {
65        View mRootView;
66        RunningProcessesView.ActiveItem mActiveItem;
67        RunningProcessesView.ViewHolder mViewHolder;
68        PendingIntent mManageIntent;
69
70        public void onClick(View v) {
71            if (mManageIntent != null) {
72                try {
73                    startIntentSender(mManageIntent.getIntentSender(), null,
74                            Intent.FLAG_ACTIVITY_NEW_TASK
75                                    | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
76                            Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
77                } catch (IntentSender.SendIntentException e) {
78                    Log.w(TAG, e);
79                } catch (IllegalArgumentException e) {
80                    Log.w(TAG, e);
81                } catch (ActivityNotFoundException e) {
82                    Log.w(TAG, e);
83                }
84            } else if (mActiveItem.mItem instanceof RunningState.ServiceItem) {
85                RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
86                stopService(new Intent().setComponent(si.mRunningService.service));
87                if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
88                    // If there was only one service, we are finishing it,
89                    // so no reason for the UI to stick around.
90                    finish();
91                } else {
92                    if (mBackgroundHandler != null) {
93                        mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
94                    }
95                }
96            } else {
97                // Heavy-weight process.  We'll do a force-stop on it.
98                mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
99                finish();
100            }
101        }
102    }
103
104    StringBuilder mBuilder = new StringBuilder(128);
105
106    HandlerThread mBackgroundThread;
107    final class BackgroundHandler extends Handler {
108        public BackgroundHandler(Looper looper) {
109            super(looper);
110        }
111
112        @Override
113        public void handleMessage(Message msg) {
114            switch (msg.what) {
115                case MSG_UPDATE_CONTENTS:
116                    Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
117                    cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
118                    mHandler.sendMessage(cmd);
119                    removeMessages(MSG_UPDATE_CONTENTS);
120                    msg = obtainMessage(MSG_UPDATE_CONTENTS);
121                    sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
122                    break;
123            }
124        }
125    };
126
127    BackgroundHandler mBackgroundHandler;
128
129    final Handler mHandler = new Handler() {
130        @Override
131        public void handleMessage(Message msg) {
132            switch (msg.what) {
133                case MSG_UPDATE_TIMES:
134                    if (mSnippetActiveItem != null) {
135                        mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
136                    }
137                    for (int i=0; i<mActiveDetails.size(); i++) {
138                        mActiveDetails.get(i).mActiveItem.updateTime(
139                                RunningServiceDetails.this, mBuilder);
140                    }
141                    removeMessages(MSG_UPDATE_TIMES);
142                    msg = obtainMessage(MSG_UPDATE_TIMES);
143                    sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
144                    break;
145                case MSG_REFRESH_UI:
146                    refreshUi(msg.arg1 != 0);
147                    break;
148            }
149        }
150    };
151
152    boolean findMergedItem() {
153        RunningState.MergedItem item = null;
154        ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
155        if (newItems != null) {
156            for (int i=0; i<newItems.size(); i++) {
157                RunningState.MergedItem mi = newItems.get(i);
158                if (mi.mProcess.mUid == mUid
159                        && mi.mProcess.mProcessName.equals(mProcessName)) {
160                    item = mi;
161                    break;
162                }
163            }
164        }
165        if (mMergedItem != item) {
166            mMergedItem = item;
167            return true;
168        }
169        return false;
170    }
171
172    void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
173        if (mNumServices == 0) {
174            mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
175                    mAllDetails, false);
176            mServicesHeader.setText(R.string.runningservicedetails_services_title);
177            mAllDetails.addView(mServicesHeader);
178        }
179        mNumServices++;
180
181        RunningState.BaseItem bi = si != null ? si : mi;
182
183        ActiveDetail detail = new ActiveDetail();
184        View root = mInflater.inflate(R.layout.running_service_details_service,
185                mAllDetails, false);
186        mAllDetails.addView(root);
187        detail.mRootView = root;
188        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
189        detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
190
191        if (si != null && si.mRunningService.clientLabel != 0) {
192            detail.mManageIntent = mAm.getRunningServiceControlPanel(
193                    si.mRunningService.service);
194        }
195
196        TextView description = (TextView)root.findViewById(R.id.comp_description);
197        if (si != null && si.mServiceInfo.descriptionRes != 0) {
198            description.setText(getPackageManager().getText(
199                    si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
200                    si.mServiceInfo.applicationInfo));
201        } else {
202            if (detail.mManageIntent != null) {
203                try {
204                    Resources clientr = getPackageManager().getResourcesForApplication(
205                            si.mRunningService.clientPackage);
206                    String label = clientr.getString(si.mRunningService.clientLabel);
207                    description.setText(getString(R.string.service_manage_description,
208                            label));
209                } catch (PackageManager.NameNotFoundException e) {
210                }
211            } else {
212                description.setText(getText(si != null
213                        ? R.string.service_stop_description
214                        : R.string.heavy_weight_stop_description));
215            }
216        }
217
218        View button = root.findViewById(R.id.right_button);
219        button.setOnClickListener(detail);
220        ((TextView)button).setText(getText(detail.mManageIntent != null
221                ? R.string.service_manage : R.string.service_stop));
222        root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
223
224        mActiveDetails.add(detail);
225    }
226
227    void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
228        if (mNumProcesses == 0) {
229            mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
230                    mAllDetails, false);
231            mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
232            mAllDetails.addView(mProcessesHeader);
233        }
234        mNumProcesses++;
235
236        ActiveDetail detail = new ActiveDetail();
237        View root = mInflater.inflate(R.layout.running_service_details_process,
238                mAllDetails, false);
239        mAllDetails.addView(root);
240        detail.mRootView = root;
241        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
242        detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
243
244        TextView description = (TextView)root.findViewById(R.id.comp_description);
245        if (isMain) {
246            description.setText(R.string.main_running_process_description);
247        } else {
248            int textid = 0;
249            CharSequence label = null;
250            ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
251            final ComponentName comp = rpi.importanceReasonComponent;
252            //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
253            //        + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
254            switch (rpi.importanceReasonCode) {
255                case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
256                    textid = R.string.process_provider_in_use_description;
257                    List<ProviderInfo> providers = null;
258                    if (comp != null) {
259                        providers = getPackageManager()
260                                .queryContentProviders(comp.getPackageName(),
261                                        rpi.uid, 0);
262                    }
263                    if (providers != null) {
264                        for (int j=0; j<providers.size(); j++) {
265                            ProviderInfo prov = providers.get(j);
266                            if (comp.getClassName().equals(prov.name)) {
267                                label = RunningState.makeLabel(getPackageManager(),
268                                        prov.name, prov);
269                                break;
270                            }
271                        }
272                    }
273                    break;
274                case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
275                    textid = R.string.process_service_in_use_description;
276                    if (rpi.importanceReasonComponent != null) {
277                        try {
278                            ServiceInfo serv = getPackageManager().getServiceInfo(
279                                    rpi.importanceReasonComponent, 0);
280                            label = RunningState.makeLabel(getPackageManager(),
281                                    serv.name, serv);
282                        } catch (NameNotFoundException e) {
283                        }
284                    }
285                    break;
286            }
287            if (textid != 0 && label != null) {
288                description.setText(getString(textid, label));
289            }
290        }
291
292        mActiveDetails.add(detail);
293    }
294
295    void addDetailViews() {
296        for (int i=mActiveDetails.size()-1; i>=0; i--) {
297            mAllDetails.removeView(mActiveDetails.get(i).mRootView);
298        }
299        mActiveDetails.clear();
300
301        if (mServicesHeader != null) {
302            mAllDetails.removeView(mServicesHeader);
303            mServicesHeader = null;
304        }
305
306        if (mProcessesHeader != null) {
307            mAllDetails.removeView(mProcessesHeader);
308            mProcessesHeader = null;
309        }
310
311        mNumServices = mNumProcesses = 0;
312
313        if (mMergedItem != null) {
314            for (int i=0; i<mMergedItem.mServices.size(); i++) {
315                addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
316            }
317
318            if (mMergedItem.mServices.size() <= 0) {
319                // This item does not have any services, so it must be
320                // a heavy-weight process...  we will put a fake service
321                // entry for it, to allow the user to "stop" it.
322                addServiceDetailsView(null, mMergedItem);
323            }
324
325            for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
326                RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
327                        : mMergedItem.mOtherProcesses.get(i);
328                if (pi.mPid <= 0) {
329                    continue;
330                }
331
332                addProcessDetailsView(pi, i < 0);
333            }
334        }
335    }
336
337    void refreshUi(boolean dataChanged) {
338        if (findMergedItem()) {
339            dataChanged = true;
340        }
341        if (dataChanged) {
342            if (mMergedItem != null) {
343                mSnippetActiveItem = mSnippetViewHolder.bind(mState,
344                        mMergedItem, mBuilder);
345            } else if (mSnippetActiveItem != null) {
346                // Clear whatever is currently being shown.
347                mSnippetActiveItem.mHolder.size.setText("");
348                mSnippetActiveItem.mHolder.uptime.setText("");
349                mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
350            } else {
351                // No merged item, never had one.  Nothing to do.
352                finish();
353                return;
354            }
355            addDetailViews();
356        }
357    }
358
359    @Override
360    protected void onCreate(Bundle savedInstanceState) {
361        super.onCreate(savedInstanceState);
362
363        mUid = getIntent().getIntExtra(KEY_UID, 0);
364        mProcessName = getIntent().getStringExtra(KEY_PROCESS);
365
366        mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
367        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
368
369        mState = (RunningState)getLastNonConfigurationInstance();
370        if (mState == null) {
371            mState = new RunningState();
372        }
373
374        setContentView(R.layout.running_service_details);
375
376        mAllDetails = (ViewGroup)findViewById(R.id.all_details);
377        mSnippet = (ViewGroup)findViewById(R.id.snippet);
378        mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
379        mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
380        mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
381    }
382
383    @Override
384    protected void onPause() {
385        super.onPause();
386        mHandler.removeMessages(MSG_UPDATE_TIMES);
387        if (mBackgroundThread != null) {
388            mBackgroundThread.quit();
389            mBackgroundThread = null;
390            mBackgroundHandler = null;
391        }
392    }
393
394    @Override
395    protected void onResume() {
396        super.onResume();
397        refreshUi(mState.update(this, mAm));
398        mBackgroundThread = new HandlerThread("RunningServices");
399        mBackgroundThread.start();
400        mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
401        mHandler.removeMessages(MSG_UPDATE_TIMES);
402        Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
403        mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
404        mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
405        msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
406        mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
407    }
408
409    @Override
410    public Object onRetainNonConfigurationInstance() {
411        return mState;
412    }
413
414    @Override
415    protected void onSaveInstanceState(Bundle outState) {
416        super.onSaveInstanceState(outState);
417    }
418}
419