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