• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.settings.applications;
2 
3 import com.android.settings.R;
4 
5 import android.app.Activity;
6 import android.app.ActivityManager;
7 import android.app.AlertDialog;
8 import android.app.ApplicationErrorReport;
9 import android.app.Dialog;
10 import android.app.DialogFragment;
11 import android.app.Fragment;
12 import android.app.PendingIntent;
13 import android.content.ActivityNotFoundException;
14 import android.content.ComponentName;
15 import android.content.Context;
16 import android.content.DialogInterface;
17 import android.content.Intent;
18 import android.content.IntentSender;
19 import android.content.pm.ApplicationInfo;
20 import android.content.pm.PackageManager;
21 import android.content.pm.ProviderInfo;
22 import android.content.pm.ServiceInfo;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.content.res.Resources;
25 import android.os.Bundle;
26 import android.os.Debug;
27 import android.os.Handler;
28 import android.os.SystemClock;
29 import android.provider.Settings;
30 import android.util.Log;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.Button;
35 import android.widget.TextView;
36 
37 import java.io.File;
38 import java.io.FileInputStream;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.util.ArrayList;
42 
43 public class RunningServiceDetails extends Fragment
44         implements RunningState.OnRefreshUiListener {
45     static final String TAG = "RunningServicesDetails";
46 
47     static final String KEY_UID = "uid";
48     static final String KEY_PROCESS = "process";
49     static final String KEY_BACKGROUND = "background";
50 
51     static final int DIALOG_CONFIRM_STOP = 1;
52 
53     ActivityManager mAm;
54     LayoutInflater mInflater;
55 
56     RunningState mState;
57     boolean mHaveData;
58 
59     int mUid;
60     String mProcessName;
61     boolean mShowBackground;
62 
63     RunningState.MergedItem mMergedItem;
64 
65     View mRootView;
66     ViewGroup mAllDetails;
67     ViewGroup mSnippet;
68     RunningProcessesView.ActiveItem mSnippetActiveItem;
69     RunningProcessesView.ViewHolder mSnippetViewHolder;
70 
71     int mNumServices, mNumProcesses;
72 
73     TextView mServicesHeader;
74     TextView mProcessesHeader;
75     final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
76 
77     class ActiveDetail implements View.OnClickListener {
78         View mRootView;
79         Button mStopButton;
80         Button mReportButton;
81         RunningState.ServiceItem mServiceItem;
82         RunningProcessesView.ActiveItem mActiveItem;
83         RunningProcessesView.ViewHolder mViewHolder;
84         PendingIntent mManageIntent;
85         ComponentName mInstaller;
86 
stopActiveService(boolean confirmed)87         void stopActiveService(boolean confirmed) {
88             RunningState.ServiceItem si = mServiceItem;
89             if (!confirmed) {
90                 if ((si.mServiceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
91                     showConfirmStopDialog(si.mRunningService.service);
92                     return;
93                 }
94             }
95             getActivity().stopService(new Intent().setComponent(si.mRunningService.service));
96             if (mMergedItem == null) {
97                 // If this is gone, we are gone.
98                 mState.updateNow();
99                 finish();
100             } else if (!mShowBackground && mMergedItem.mServices.size() <= 1) {
101                 // If there was only one service, we are finishing it,
102                 // so no reason for the UI to stick around.
103                 mState.updateNow();
104                 finish();
105             } else {
106                 mState.updateNow();
107             }
108         }
109 
onClick(View v)110         public void onClick(View v) {
111             if (v == mReportButton) {
112                 ApplicationErrorReport report = new ApplicationErrorReport();
113                 report.type = ApplicationErrorReport.TYPE_RUNNING_SERVICE;
114                 report.packageName = mServiceItem.mServiceInfo.packageName;
115                 report.installerPackageName = mInstaller.getPackageName();
116                 report.processName = mServiceItem.mRunningService.process;
117                 report.time = System.currentTimeMillis();
118                 report.systemApp = (mServiceItem.mServiceInfo.applicationInfo.flags
119                         & ApplicationInfo.FLAG_SYSTEM) != 0;
120                 ApplicationErrorReport.RunningServiceInfo info
121                         = new ApplicationErrorReport.RunningServiceInfo();
122                 if (mActiveItem.mFirstRunTime >= 0) {
123                     info.durationMillis = SystemClock.elapsedRealtime()-mActiveItem.mFirstRunTime;
124                 } else {
125                     info.durationMillis = -1;
126                 }
127                 ComponentName comp = new ComponentName(mServiceItem.mServiceInfo.packageName,
128                         mServiceItem.mServiceInfo.name);
129                 File filename = getActivity().getFileStreamPath("service_dump.txt");
130                 FileOutputStream output = null;
131                 try {
132                     output = new FileOutputStream(filename);
133                     Debug.dumpService("activity", output.getFD(),
134                             new String[] { "-a", "service", comp.flattenToString() });
135                 } catch (IOException e) {
136                     Log.w(TAG, "Can't dump service: " + comp, e);
137                 } finally {
138                     if (output != null) try { output.close(); } catch (IOException e) {}
139                 }
140                 FileInputStream input = null;
141                 try {
142                     input = new FileInputStream(filename);
143                     byte[] buffer = new byte[(int) filename.length()];
144                     input.read(buffer);
145                     info.serviceDetails = new String(buffer);
146                 } catch (IOException e) {
147                     Log.w(TAG, "Can't read service dump: " + comp, e);
148                 } finally {
149                     if (input != null) try { input.close(); } catch (IOException e) {}
150                 }
151                 filename.delete();
152                 Log.i(TAG, "Details: " + info.serviceDetails);
153                 report.runningServiceInfo = info;
154                 Intent result = new Intent(Intent.ACTION_APP_ERROR);
155                 result.setComponent(mInstaller);
156                 result.putExtra(Intent.EXTRA_BUG_REPORT, report);
157                 result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
158                 startActivity(result);
159                 return;
160             }
161 
162             if (mManageIntent != null) {
163                 try {
164                     getActivity().startIntentSender(mManageIntent.getIntentSender(), null,
165                             Intent.FLAG_ACTIVITY_NEW_TASK
166                                     | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
167                             Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
168                 } catch (IntentSender.SendIntentException e) {
169                     Log.w(TAG, e);
170                 } catch (IllegalArgumentException e) {
171                     Log.w(TAG, e);
172                 } catch (ActivityNotFoundException e) {
173                     Log.w(TAG, e);
174                 }
175             } else if (mServiceItem != null) {
176                 stopActiveService(false);
177             } else if (mActiveItem.mItem.mBackground) {
178                 // Background process.  Just kill it.
179                 mAm.killBackgroundProcesses(mActiveItem.mItem.mPackageInfo.packageName);
180                 finish();
181             } else {
182                 // Heavy-weight process.  We'll do a force-stop on it.
183                 mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
184                 finish();
185             }
186         }
187     }
188 
189     StringBuilder mBuilder = new StringBuilder(128);
190 
findMergedItem()191     boolean findMergedItem() {
192         RunningState.MergedItem item = null;
193         ArrayList<RunningState.MergedItem> newItems = mShowBackground
194                 ? mState.getCurrentBackgroundItems() : mState.getCurrentMergedItems();
195         if (newItems != null) {
196             for (int i=0; i<newItems.size(); i++) {
197                 RunningState.MergedItem mi = newItems.get(i);
198                 if (mi.mProcess.mUid == mUid
199                         && mi.mProcess.mProcessName.equals(mProcessName)) {
200                     item = mi;
201                     break;
202                 }
203             }
204         }
205 
206         if (mMergedItem != item) {
207             mMergedItem = item;
208             return true;
209         }
210         return false;
211     }
212 
addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi)213     void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
214         if (mNumServices == 0) {
215             mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
216                     mAllDetails, false);
217             mServicesHeader.setText(R.string.runningservicedetails_services_title);
218             mAllDetails.addView(mServicesHeader);
219         }
220         mNumServices++;
221 
222         RunningState.BaseItem bi = si != null ? si : mi;
223 
224         ActiveDetail detail = new ActiveDetail();
225         View root = mInflater.inflate(R.layout.running_service_details_service,
226                 mAllDetails, false);
227         mAllDetails.addView(root);
228         detail.mRootView = root;
229         detail.mServiceItem = si;
230         detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
231         detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
232 
233         if (si != null && si.mRunningService.clientLabel != 0) {
234             detail.mManageIntent = mAm.getRunningServiceControlPanel(
235                     si.mRunningService.service);
236         }
237 
238         TextView description = (TextView)root.findViewById(R.id.comp_description);
239         if (si != null && si.mServiceInfo.descriptionRes != 0) {
240             description.setText(getActivity().getPackageManager().getText(
241                     si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
242                     si.mServiceInfo.applicationInfo));
243         } else {
244             if (mi.mBackground) {
245                 description.setText(R.string.background_process_stop_description);
246             } else if (detail.mManageIntent != null) {
247                 try {
248                     Resources clientr = getActivity().getPackageManager().getResourcesForApplication(
249                             si.mRunningService.clientPackage);
250                     String label = clientr.getString(si.mRunningService.clientLabel);
251                     description.setText(getActivity().getString(R.string.service_manage_description,
252                             label));
253                 } catch (PackageManager.NameNotFoundException e) {
254                 }
255             } else {
256                 description.setText(getActivity().getText(si != null
257                         ? R.string.service_stop_description
258                         : R.string.heavy_weight_stop_description));
259             }
260         }
261 
262         detail.mStopButton = (Button)root.findViewById(R.id.left_button);
263         detail.mStopButton.setOnClickListener(detail);
264         detail.mStopButton.setText(getActivity().getText(detail.mManageIntent != null
265                 ? R.string.service_manage : R.string.service_stop));
266 
267         detail.mReportButton = (Button)root.findViewById(R.id.right_button);
268         detail.mReportButton.setOnClickListener(detail);
269         detail.mReportButton.setText(com.android.internal.R.string.report);
270         // check if error reporting is enabled in secure settings
271         int enabled = Settings.Secure.getInt(getActivity().getContentResolver(),
272                 Settings.Secure.SEND_ACTION_APP_ERROR, 0);
273         if (enabled != 0 && si != null) {
274             detail.mInstaller = ApplicationErrorReport.getErrorReportReceiver(
275                     getActivity(), si.mServiceInfo.packageName,
276                     si.mServiceInfo.applicationInfo.flags);
277             detail.mReportButton.setEnabled(detail.mInstaller != null);
278         } else {
279             detail.mReportButton.setEnabled(false);
280         }
281 
282         mActiveDetails.add(detail);
283     }
284 
addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain)285     void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
286         if (mNumProcesses == 0) {
287             mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
288                     mAllDetails, false);
289             mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
290             mAllDetails.addView(mProcessesHeader);
291         }
292         mNumProcesses++;
293 
294         ActiveDetail detail = new ActiveDetail();
295         View root = mInflater.inflate(R.layout.running_service_details_process,
296                 mAllDetails, false);
297         mAllDetails.addView(root);
298         detail.mRootView = root;
299         detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
300         detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
301 
302         TextView description = (TextView)root.findViewById(R.id.comp_description);
303         if (isMain) {
304             description.setText(R.string.main_running_process_description);
305         } else {
306             int textid = 0;
307             CharSequence label = null;
308             ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
309             final ComponentName comp = rpi.importanceReasonComponent;
310             //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
311             //        + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
312             switch (rpi.importanceReasonCode) {
313                 case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
314                     textid = R.string.process_provider_in_use_description;
315                     if (rpi.importanceReasonComponent != null) {
316                         try {
317                             ProviderInfo prov = getActivity().getPackageManager().getProviderInfo(
318                                     rpi.importanceReasonComponent, 0);
319                             label = RunningState.makeLabel(getActivity().getPackageManager(),
320                                     prov.name, prov);
321                         } catch (NameNotFoundException e) {
322                         }
323                     }
324                     break;
325                 case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
326                     textid = R.string.process_service_in_use_description;
327                     if (rpi.importanceReasonComponent != null) {
328                         try {
329                             ServiceInfo serv = getActivity().getPackageManager().getServiceInfo(
330                                     rpi.importanceReasonComponent, 0);
331                             label = RunningState.makeLabel(getActivity().getPackageManager(),
332                                     serv.name, serv);
333                         } catch (NameNotFoundException e) {
334                         }
335                     }
336                     break;
337             }
338             if (textid != 0 && label != null) {
339                 description.setText(getActivity().getString(textid, label));
340             }
341         }
342 
343         mActiveDetails.add(detail);
344     }
345 
addDetailViews()346     void addDetailViews() {
347         for (int i=mActiveDetails.size()-1; i>=0; i--) {
348             mAllDetails.removeView(mActiveDetails.get(i).mRootView);
349         }
350         mActiveDetails.clear();
351 
352         if (mServicesHeader != null) {
353             mAllDetails.removeView(mServicesHeader);
354             mServicesHeader = null;
355         }
356 
357         if (mProcessesHeader != null) {
358             mAllDetails.removeView(mProcessesHeader);
359             mProcessesHeader = null;
360         }
361 
362         mNumServices = mNumProcesses = 0;
363 
364         if (mMergedItem != null) {
365             for (int i=0; i<mMergedItem.mServices.size(); i++) {
366                 addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
367             }
368 
369             if (mMergedItem.mServices.size() <= 0) {
370                 // This item does not have any services, so it must be
371                 // another interesting process...  we will put a fake service
372                 // entry for it, to allow the user to "stop" it.
373                 addServiceDetailsView(null, mMergedItem);
374             }
375 
376             for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
377                 RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
378                         : mMergedItem.mOtherProcesses.get(i);
379                 if (pi.mPid <= 0) {
380                     continue;
381                 }
382 
383                 addProcessDetailsView(pi, i < 0);
384             }
385         }
386     }
387 
388     void refreshUi(boolean dataChanged) {
389         if (findMergedItem()) {
390             dataChanged = true;
391         }
392         if (dataChanged) {
393             if (mMergedItem != null) {
394                 mSnippetActiveItem = mSnippetViewHolder.bind(mState,
395                         mMergedItem, mBuilder);
396             } else if (mSnippetActiveItem != null) {
397                 // Clear whatever is currently being shown.
398                 mSnippetActiveItem.mHolder.size.setText("");
399                 mSnippetActiveItem.mHolder.uptime.setText("");
400                 mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
401             } else {
402                 // No merged item, never had one.  Nothing to do.
403                 finish();
404                 return;
405             }
406             addDetailViews();
407         }
408     }
409 
410     private void finish() {
411         (new Handler()).post(new Runnable() {
412             @Override
413             public void run() {
414                 Activity a = getActivity();
415                 if (a != null) {
416                     a.onBackPressed();
417                 }
418             }
419         });
420     }
421 
422     @Override
423     public void onCreate(Bundle savedInstanceState) {
424         super.onCreate(savedInstanceState);
425 
426         mUid = getArguments().getInt(KEY_UID, 0);
427         mProcessName = getArguments().getString(KEY_PROCESS);
428         mShowBackground = getArguments().getBoolean(KEY_BACKGROUND, false);
429 
430         mAm = (ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE);
431         mInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
432 
433         mState = RunningState.getInstance(getActivity());
434     }
435 
436     @Override
437     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
438         View view = mRootView = inflater.inflate(R.layout.running_service_details, null);
439 
440         mAllDetails = (ViewGroup)view.findViewById(R.id.all_details);
441         mSnippet = (ViewGroup)view.findViewById(R.id.snippet);
442         mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
443         mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
444 
445         // We want to retrieve the data right now, so any active managed
446         // dialog that gets created can find it.
447         ensureData();
448 
449         return view;
450     }
451 
452     @Override
453     public void onPause() {
454         super.onPause();
455         mHaveData = false;
456         mState.pause();
457     }
458 
459     @Override
460     public void onResume() {
461         super.onResume();
462         ensureData();
463     }
464 
465     ActiveDetail activeDetailForService(ComponentName comp) {
466         for (int i=0; i<mActiveDetails.size(); i++) {
467             ActiveDetail ad = mActiveDetails.get(i);
468             if (ad.mServiceItem != null && ad.mServiceItem.mRunningService != null
469                     && comp.equals(ad.mServiceItem.mRunningService.service)) {
470                 return ad;
471             }
472         }
473         return null;
474     }
475 
476     private void showConfirmStopDialog(ComponentName comp) {
477         DialogFragment newFragment = MyAlertDialogFragment.newConfirmStop(
478                 DIALOG_CONFIRM_STOP, comp);
479         newFragment.setTargetFragment(this, 0);
480         newFragment.show(getFragmentManager(), "confirmstop");
481     }
482 
483     public static class MyAlertDialogFragment extends DialogFragment {
484 
485         public static MyAlertDialogFragment newConfirmStop(int id, ComponentName comp) {
486             MyAlertDialogFragment frag = new MyAlertDialogFragment();
487             Bundle args = new Bundle();
488             args.putInt("id", id);
489             args.putParcelable("comp", comp);
490             frag.setArguments(args);
491             return frag;
492         }
493 
494         RunningServiceDetails getOwner() {
495             return (RunningServiceDetails)getTargetFragment();
496         }
497 
498         @Override
499         public Dialog onCreateDialog(Bundle savedInstanceState) {
500             int id = getArguments().getInt("id");
501             switch (id) {
502                 case DIALOG_CONFIRM_STOP: {
503                     final ComponentName comp = (ComponentName)getArguments().getParcelable("comp");
504                     if (getOwner().activeDetailForService(comp) == null) {
505                         return null;
506                     }
507 
508                     return new AlertDialog.Builder(getActivity())
509                             .setTitle(getActivity().getString(R.string.runningservicedetails_stop_dlg_title))
510                             .setIcon(android.R.drawable.ic_dialog_alert)
511                             .setMessage(getActivity().getString(R.string.runningservicedetails_stop_dlg_text))
512                             .setPositiveButton(R.string.dlg_ok,
513                                     new DialogInterface.OnClickListener() {
514                                 public void onClick(DialogInterface dialog, int which) {
515                                     ActiveDetail ad = getOwner().activeDetailForService(comp);
516                                     if (ad != null) {
517                                         ad.stopActiveService(true);
518                                     }
519                                 }
520                             })
521                             .setNegativeButton(R.string.dlg_cancel, null)
522                             .create();
523                 }
524             }
525             throw new IllegalArgumentException("unknown id " + id);
526         }
527     }
528 
529     void ensureData() {
530         if (!mHaveData) {
531             mHaveData = true;
532             mState.resume(this);
533 
534             // We want to go away if the service being shown no longer exists,
535             // so we need to ensure we have done the initial data retrieval before
536             // showing our ui.
537             mState.waitForData();
538 
539             // And since we know we have the data, let's show the UI right away
540             // to avoid flicker.
541             refreshUi(true);
542         }
543     }
544 
545     void updateTimes() {
546         if (mSnippetActiveItem != null) {
547             mSnippetActiveItem.updateTime(getActivity(), mBuilder);
548         }
549         for (int i=0; i<mActiveDetails.size(); i++) {
550             mActiveDetails.get(i).mActiveItem.updateTime(getActivity(), mBuilder);
551         }
552     }
553 
554     @Override
555     public void onRefreshUi(int what) {
556         if (getActivity() == null) return;
557         switch (what) {
558             case REFRESH_TIME:
559                 updateTimes();
560                 break;
561             case REFRESH_DATA:
562                 refreshUi(false);
563                 updateTimes();
564                 break;
565             case REFRESH_STRUCTURE:
566                 refreshUi(true);
567                 updateTimes();
568                 break;
569         }
570     }
571 }
572