RunningServiceDetails.java revision c01b0c83fca571229621d16b757a46dc0fae7dfe
153cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtpackage com.android.settings.applications;
253cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport com.android.settings.R;
453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
553cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.app.Activity;
653cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.app.ActivityManager;
753cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.app.PendingIntent;
853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.ActivityNotFoundException;
953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.ComponentName;
1053cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.Context;
1153cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.Intent;
1253cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.IntentSender;
1353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.pm.PackageManager;
1453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.pm.ProviderInfo;
1553cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.pm.ServiceInfo;
1653cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.pm.PackageManager.NameNotFoundException;
1753cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.content.res.Resources;
1853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.os.Bundle;
1953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.os.Handler;
2053cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.os.HandlerThread;
2153cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.os.Looper;
2253cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.os.Message;
2353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.util.Log;
2453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.view.LayoutInflater;
2553cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.view.View;
2653cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.view.ViewGroup;
2753cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.widget.LinearLayout;
2853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport android.widget.TextView;
2953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
3053cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport java.util.ArrayList;
3153cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtimport java.util.List;
3253cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
3353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholtpublic class RunningServiceDetails extends Activity {
3453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    static final String TAG = "RunningServicesDetails";
3553cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
369349379d1acca23e7a2442549e49e9b58515d731José Fonseca    static final String KEY_UID = "uid";
3753cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    static final String KEY_PROCESS = "process";
38865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
3931747155ea3a24190277b125bd188ac8689af719Aras Pranckevicius    static final int MSG_UPDATE_TIMES = 1;
40f141fa63a4391621cc92cd2c39724a952b297a58Eric Anholt    static final int MSG_UPDATE_CONTENTS = 2;
4153cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    static final int MSG_REFRESH_UI = 3;
42865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
43865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    ActivityManager mAm;
44865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    LayoutInflater mInflater;
45865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
46d1a1ee583e7e8338243b3e9768d2fc5312a1145dIan Romanick    RunningState mState;
47d1a1ee583e7e8338243b3e9768d2fc5312a1145dIan Romanick
4853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    int mUid;
49c67016de960c988c748ffdb11247072543a8f328Ian Romanick    String mProcessName;
50c67016de960c988c748ffdb11247072543a8f328Ian Romanick
51865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    RunningState.MergedItem mMergedItem;
52865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
53865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    ViewGroup mAllDetails;
5453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    ViewGroup mSnippet;
55865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    RunningProcessesView.ActiveItem mSnippetActiveItem;
56865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    RunningProcessesView.ViewHolder mSnippetViewHolder;
57865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
58865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    int mNumServices, mNumProcesses;
5953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
60865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    TextView mServicesHeader;
618baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick    TextView mProcessesHeader;
62432b787b29202301dbfc139c3289521b0bfc3decEric Anholt    final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
6353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
6453acbd87d712555f9e7a1c304843be7b39641413Ian Romanick    class ActiveDetail implements View.OnClickListener {
65c67016de960c988c748ffdb11247072543a8f328Ian Romanick        View mRootView;
66c67016de960c988c748ffdb11247072543a8f328Ian Romanick        RunningProcessesView.ActiveItem mActiveItem;
67c67016de960c988c748ffdb11247072543a8f328Ian Romanick        RunningProcessesView.ViewHolder mViewHolder;
68c67016de960c988c748ffdb11247072543a8f328Ian Romanick        PendingIntent mManageIntent;
695533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
705533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        public void onClick(View v) {
716235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick            if (mManageIntent != null) {
726235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick                try {
73865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick                    startIntentSender(mManageIntent.getIntentSender(), null,
7453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt                            Intent.FLAG_ACTIVITY_NEW_TASK
75c67016de960c988c748ffdb11247072543a8f328Ian Romanick                                    | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
76c67016de960c988c748ffdb11247072543a8f328Ian Romanick                            Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
77865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick                } catch (IntentSender.SendIntentException e) {
78865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick                    Log.w(TAG, e);
7953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt                } catch (IllegalArgumentException e) {
808baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    Log.w(TAG, e);
818baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                } catch (ActivityNotFoundException e) {
828baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    Log.w(TAG, e);
838baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                }
848baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick            } else if (mActiveItem.mItem instanceof RunningState.ServiceItem) {
858baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
869f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul                stopService(new Intent().setComponent(si.mRunningService.service));
878baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
888baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    // If there was only one service, we are finishing it,
898baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    // so no reason for the UI to stick around.
908baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    finish();
918baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                } else {
928baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    if (mBackgroundHandler != null) {
939f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul                        mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
948baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    }
958baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                }
968baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick            } else {
97506880bc32e7bb98fd1896a9b2fe3614abab904fIan Romanick                // Heavy-weight process.  We'll do a force-stop on it.
98506880bc32e7bb98fd1896a9b2fe3614abab904fIan Romanick                mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
998baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                finish();
1008baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick            }
1018baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick        }
102432b787b29202301dbfc139c3289521b0bfc3decEric Anholt    }
103432b787b29202301dbfc139c3289521b0bfc3decEric Anholt
104432b787b29202301dbfc139c3289521b0bfc3decEric Anholt    StringBuilder mBuilder = new StringBuilder(128);
105432b787b29202301dbfc139c3289521b0bfc3decEric Anholt
106432b787b29202301dbfc139c3289521b0bfc3decEric Anholt    HandlerThread mBackgroundThread;
107432b787b29202301dbfc139c3289521b0bfc3decEric Anholt    final class BackgroundHandler extends Handler {
108432b787b29202301dbfc139c3289521b0bfc3decEric Anholt        public BackgroundHandler(Looper looper) {
109432b787b29202301dbfc139c3289521b0bfc3decEric Anholt            super(looper);
110432b787b29202301dbfc139c3289521b0bfc3decEric Anholt        }
111432b787b29202301dbfc139c3289521b0bfc3decEric Anholt
1126a1401eb889b5e535c212c414743cc7ea07f6622Eric Anholt        @Override
1136a1401eb889b5e535c212c414743cc7ea07f6622Eric Anholt        public void handleMessage(Message msg) {
114432b787b29202301dbfc139c3289521b0bfc3decEric Anholt            switch (msg.what) {
115432b787b29202301dbfc139c3289521b0bfc3decEric Anholt                case MSG_UPDATE_CONTENTS:
1168baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick                    Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
11753cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt                    cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
11853acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    mHandler.sendMessage(cmd);
11953acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    removeMessages(MSG_UPDATE_CONTENTS);
12053acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    msg = obtainMessage(MSG_UPDATE_CONTENTS);
12153acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
12253acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    break;
12353acbd87d712555f9e7a1c304843be7b39641413Ian Romanick            }
12453acbd87d712555f9e7a1c304843be7b39641413Ian Romanick        }
12553acbd87d712555f9e7a1c304843be7b39641413Ian Romanick    };
12653acbd87d712555f9e7a1c304843be7b39641413Ian Romanick
12753acbd87d712555f9e7a1c304843be7b39641413Ian Romanick    BackgroundHandler mBackgroundHandler;
12853acbd87d712555f9e7a1c304843be7b39641413Ian Romanick
12953acbd87d712555f9e7a1c304843be7b39641413Ian Romanick    final Handler mHandler = new Handler() {
13053acbd87d712555f9e7a1c304843be7b39641413Ian Romanick        @Override
13153acbd87d712555f9e7a1c304843be7b39641413Ian Romanick        public void handleMessage(Message msg) {
13253acbd87d712555f9e7a1c304843be7b39641413Ian Romanick            switch (msg.what) {
13353acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                case MSG_UPDATE_TIMES:
13453acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    if (mSnippetActiveItem != null) {
13553acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                        mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
13653acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    }
13753acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    for (int i=0; i<mActiveDetails.size(); i++) {
13853acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                        mActiveDetails.get(i).mActiveItem.updateTime(
13953acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                                RunningServiceDetails.this, mBuilder);
14053acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    }
14153acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    removeMessages(MSG_UPDATE_TIMES);
14253acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    msg = obtainMessage(MSG_UPDATE_TIMES);
14353acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
14453acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    break;
14553acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                case MSG_REFRESH_UI:
14653acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    refreshUi(msg.arg1 != 0);
14753acbd87d712555f9e7a1c304843be7b39641413Ian Romanick                    break;
14853acbd87d712555f9e7a1c304843be7b39641413Ian Romanick            }
14953acbd87d712555f9e7a1c304843be7b39641413Ian Romanick        }
15053acbd87d712555f9e7a1c304843be7b39641413Ian Romanick    };
15153acbd87d712555f9e7a1c304843be7b39641413Ian Romanick
152c67016de960c988c748ffdb11247072543a8f328Ian Romanick    boolean findMergedItem() {
153c67016de960c988c748ffdb11247072543a8f328Ian Romanick        RunningState.MergedItem item = null;
154c67016de960c988c748ffdb11247072543a8f328Ian Romanick        ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
155c67016de960c988c748ffdb11247072543a8f328Ian Romanick        if (newItems != null) {
156c67016de960c988c748ffdb11247072543a8f328Ian Romanick            for (int i=0; i<newItems.size(); i++) {
157c67016de960c988c748ffdb11247072543a8f328Ian Romanick                RunningState.MergedItem mi = newItems.get(i);
158c67016de960c988c748ffdb11247072543a8f328Ian Romanick                if (mi.mProcess.mUid == mUid
159c67016de960c988c748ffdb11247072543a8f328Ian Romanick                        && mi.mProcess.mProcessName.equals(mProcessName)) {
1609f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul                    item = mi;
1619f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul                    break;
162c67016de960c988c748ffdb11247072543a8f328Ian Romanick                }
163c67016de960c988c748ffdb11247072543a8f328Ian Romanick            }
164c67016de960c988c748ffdb11247072543a8f328Ian Romanick        }
165c67016de960c988c748ffdb11247072543a8f328Ian Romanick        if (mMergedItem != item) {
166c67016de960c988c748ffdb11247072543a8f328Ian Romanick            mMergedItem = item;
167c67016de960c988c748ffdb11247072543a8f328Ian Romanick            return true;
168c67016de960c988c748ffdb11247072543a8f328Ian Romanick        }
169c67016de960c988c748ffdb11247072543a8f328Ian Romanick        return false;
170c67016de960c988c748ffdb11247072543a8f328Ian Romanick    }
171c67016de960c988c748ffdb11247072543a8f328Ian Romanick
172c67016de960c988c748ffdb11247072543a8f328Ian Romanick    void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
173c67016de960c988c748ffdb11247072543a8f328Ian Romanick        if (mNumServices == 0) {
174c67016de960c988c748ffdb11247072543a8f328Ian Romanick            mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
175c67016de960c988c748ffdb11247072543a8f328Ian Romanick                    mAllDetails, false);
176c67016de960c988c748ffdb11247072543a8f328Ian Romanick            mServicesHeader.setText(R.string.runningservicedetails_services_title);
177c67016de960c988c748ffdb11247072543a8f328Ian Romanick            mAllDetails.addView(mServicesHeader);
178c67016de960c988c748ffdb11247072543a8f328Ian Romanick        }
179ee7666b5ac2fc7de64baf60835271e15baf89474Eric Anholt        mNumServices++;
180c67016de960c988c748ffdb11247072543a8f328Ian Romanick
181c67016de960c988c748ffdb11247072543a8f328Ian Romanick        RunningState.BaseItem bi = si != null ? si : mi;
182c67016de960c988c748ffdb11247072543a8f328Ian Romanick
183c67016de960c988c748ffdb11247072543a8f328Ian Romanick        ActiveDetail detail = new ActiveDetail();
184c67016de960c988c748ffdb11247072543a8f328Ian Romanick        View root = mInflater.inflate(R.layout.running_service_details_service,
185c67016de960c988c748ffdb11247072543a8f328Ian Romanick                mAllDetails, false);
186c67016de960c988c748ffdb11247072543a8f328Ian Romanick        mAllDetails.addView(root);
187c67016de960c988c748ffdb11247072543a8f328Ian Romanick        detail.mRootView = root;
188c67016de960c988c748ffdb11247072543a8f328Ian Romanick        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
189c67016de960c988c748ffdb11247072543a8f328Ian Romanick        detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
190c67016de960c988c748ffdb11247072543a8f328Ian Romanick
191c67016de960c988c748ffdb11247072543a8f328Ian Romanick        if (si != null && si.mRunningService.clientLabel != 0) {
1929f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul            detail.mManageIntent = mAm.getRunningServiceControlPanel(
1939f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul                    si.mRunningService.service);
1949f9386d22aca8d14d1b1e6d4de9b24dcb183ca10Brian Paul        }
195c67016de960c988c748ffdb11247072543a8f328Ian Romanick
196c67016de960c988c748ffdb11247072543a8f328Ian Romanick        TextView description = (TextView)root.findViewById(R.id.comp_description);
197c67016de960c988c748ffdb11247072543a8f328Ian Romanick        if (si != null && si.mServiceInfo.descriptionRes != 0) {
198c67016de960c988c748ffdb11247072543a8f328Ian Romanick            description.setText(getPackageManager().getText(
199c67016de960c988c748ffdb11247072543a8f328Ian Romanick                    si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
200c67016de960c988c748ffdb11247072543a8f328Ian Romanick                    si.mServiceInfo.applicationInfo));
201c67016de960c988c748ffdb11247072543a8f328Ian Romanick        } else {
202c67016de960c988c748ffdb11247072543a8f328Ian Romanick            if (detail.mManageIntent != null) {
203c67016de960c988c748ffdb11247072543a8f328Ian Romanick                try {
2045533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    Resources clientr = getPackageManager().getResourcesForApplication(
2055533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            si.mRunningService.clientPackage);
2065533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    String label = clientr.getString(si.mRunningService.clientLabel);
2075533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    description.setText(getString(R.string.service_manage_description,
2085533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            label));
2095533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                } catch (PackageManager.NameNotFoundException e) {
2105533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                }
211e75dbf66d011d76b6944dc4ee55e339ee285510cEric Anholt            } else {
212e75dbf66d011d76b6944dc4ee55e339ee285510cEric Anholt                description.setText(getText(si != null
2135533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        ? R.string.service_stop_description
2145533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        : R.string.heavy_weight_stop_description));
2155533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
2165533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
2175533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2185533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        View button = root.findViewById(R.id.right_button);
2195533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        button.setOnClickListener(detail);
2205533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        ((TextView)button).setText(getText(detail.mManageIntent != null
221bc4034b243975089c06c4415d4e26edaaaec7a46Eric Anholt                ? R.string.service_manage : R.string.service_stop));
222bc4034b243975089c06c4415d4e26edaaaec7a46Eric Anholt        root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
223bc4034b243975089c06c4415d4e26edaaaec7a46Eric Anholt
2245533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        mActiveDetails.add(detail);
2255533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    }
2265533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2275533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
228bc4034b243975089c06c4415d4e26edaaaec7a46Eric Anholt        if (mNumProcesses == 0) {
2295533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
2305533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    mAllDetails, false);
2315533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
2325533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            mAllDetails.addView(mProcessesHeader);
2335533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
2345533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        mNumProcesses++;
2355533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2365533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        ActiveDetail detail = new ActiveDetail();
2375533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        View root = mInflater.inflate(R.layout.running_service_details_process,
2385533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                mAllDetails, false);
2395533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        mAllDetails.addView(root);
2405533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        detail.mRootView = root;
2415533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
2425533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
2435533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2445533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        TextView description = (TextView)root.findViewById(R.id.comp_description);
2455533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        if (isMain) {
2465533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            description.setText(R.string.main_running_process_description);
2475533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        } else {
2485533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            int textid = 0;
2495533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            CharSequence label = null;
2505533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
2515533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            final ComponentName comp = rpi.importanceReasonComponent;
2525533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
2535533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            //        + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
2545533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            switch (rpi.importanceReasonCode) {
2555533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
2565533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    textid = R.string.process_provider_in_use_description;
2575533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    List<ProviderInfo> providers = null;
2585533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    if (comp != null) {
2595533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        providers = getPackageManager()
2605533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                .queryContentProviders(comp.getPackageName(),
2615e9ac94cc44ef4f97063d7b696411b2a4be16f36Eric Anholt                                        rpi.uid, 0);
2625e9ac94cc44ef4f97063d7b696411b2a4be16f36Eric Anholt                    }
2635e9ac94cc44ef4f97063d7b696411b2a4be16f36Eric Anholt                    if (providers != null) {
2645e9ac94cc44ef4f97063d7b696411b2a4be16f36Eric Anholt                        for (int j=0; j<providers.size(); j++) {
2655e9ac94cc44ef4f97063d7b696411b2a4be16f36Eric Anholt                            ProviderInfo prov = providers.get(j);
2665533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            if (comp.getClassName().equals(prov.name)) {
2675533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                label = RunningState.makeLabel(getPackageManager(),
2685533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                        prov.name, prov);
2695533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                break;
2705533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            }
2715533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        }
2725533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    }
2735533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    break;
2745533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
2755533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    textid = R.string.process_service_in_use_description;
2765533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    if (rpi.importanceReasonComponent != null) {
2775533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        try {
2785533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            ServiceInfo serv = getPackageManager().getServiceInfo(
2795533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                    rpi.importanceReasonComponent, 0);
2805533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                            label = RunningState.makeLabel(getPackageManager(),
2815533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                                    serv.name, serv);
2825533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        } catch (NameNotFoundException e) {
2835533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        }
2845533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    }
2855533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    break;
2865533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
2875533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            if (textid != 0 && label != null) {
2885533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                description.setText(getString(textid, label));
2895533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
2905533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
2915533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2925533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        mActiveDetails.add(detail);
2935533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    }
2945533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
2955533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    void addDetailViews() {
2964dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        for (int i=mActiveDetails.size()-1; i>=0; i--) {
2975533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            mAllDetails.removeView(mActiveDetails.get(i).mRootView);
2985533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
2995533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        mActiveDetails.clear();
3005533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
3014dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        if (mServicesHeader != null) {
3024dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri            mAllDetails.removeView(mServicesHeader);
3034dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri            mServicesHeader = null;
3044dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        }
3054dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri
3064dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        if (mProcessesHeader != null) {
3075533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            mAllDetails.removeView(mProcessesHeader);
3084dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri            mProcessesHeader = null;
3095533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
3104dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri
3114dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        mNumServices = mNumProcesses = 0;
3124dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri
3134dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri        if (mMergedItem != null) {
3145533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            for (int i=0; i<mMergedItem.mServices.size(); i++) {
3155533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
3164dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri            }
3174dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri
3184dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri            if (mMergedItem.mServices.size() <= 0) {
3194dfb89904c0a3d2166e9a3fc0253a254680e91bcLuca Barbieri                // This item does not have any services, so it must be
3205533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                // a heavy-weight process...  we will put a fake service
3215533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                // entry for it, to allow the user to "stop" it.
3225533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                addServiceDetailsView(null, mMergedItem);
3235533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
3245533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
3255533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
3265533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
3275533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        : mMergedItem.mOtherProcesses.get(i);
3285533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                if (pi.mPid <= 0) {
3295533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                    continue;
3305533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                }
3315533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
3325533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                addProcessDetailsView(pi, i < 0);
3335533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
3345533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
3355533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    }
3365533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
3375533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    void refreshUi(boolean dataChanged) {
3385533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        if (findMergedItem()) {
3395533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            dataChanged = true;
3405533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
3415533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        if (dataChanged) {
3425533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            if (mMergedItem != null) {
3435533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                mSnippetActiveItem = mSnippetViewHolder.bind(mState,
3445533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                        mMergedItem, mBuilder);
3455533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            } else if (mSnippetActiveItem != null) {
3465533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                // Clear whatever is currently being shown.
347368dc76f04e19f5070d1f41795ea8cde2964639fKenneth Graunke                mSnippetActiveItem.mHolder.size.setText("");
3485533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                mSnippetActiveItem.mHolder.uptime.setText("");
3495533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
3505533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            } else {
3515533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                // No merged item, never had one.  Nothing to do.
3525533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                finish();
3535533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt                return;
3545533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            }
3555533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt            addDetailViews();
3565533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        }
3575533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    }
3585533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt
3595533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    @Override
3605533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt    protected void onCreate(Bundle savedInstanceState) {
3615533c6e38030ff6e26375a1a4e4bfa9ab2204d4cEric Anholt        super.onCreate(savedInstanceState);
362865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
36353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt        mUid = getIntent().getIntExtra(KEY_UID, 0);
364865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick        mProcessName = getIntent().getStringExtra(KEY_PROCESS);
3658baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick
3668baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick        mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
3678baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
368865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
369c22dee721695402d9f2678c100d2fff5c0c3f21fEric Anholt        mState = (RunningState)getLastNonConfigurationInstance();
370c22dee721695402d9f2678c100d2fff5c0c3f21fEric Anholt        if (mState == null) {
371ee7666b5ac2fc7de64baf60835271e15baf89474Eric Anholt            mState = new RunningState();
3728baf21b1a4d50efca086679cc43bb0cfc3fee03aIan Romanick        }
37353cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
37453cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt        setContentView(R.layout.running_service_details);
37553cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
3766235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mAllDetails = (ViewGroup)findViewById(R.id.all_details);
3776235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mSnippet = (ViewGroup)findViewById(R.id.snippet);
3786235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
3796235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
3806235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
3816235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    }
3826235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick
3836235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    @Override
3846235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    protected void onPause() {
3856235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        super.onPause();
3866235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mHandler.removeMessages(MSG_UPDATE_TIMES);
3876235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        if (mBackgroundThread != null) {
3886235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick            mBackgroundThread.quit();
3896235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick            mBackgroundThread = null;
3906235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick            mBackgroundHandler = null;
3916235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        }
3926235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    }
3936235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick
3946235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    @Override
3956235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick    protected void onResume() {
3966235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        super.onResume();
3976235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        refreshUi(mState.update(this, mAm));
3986235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mBackgroundThread = new HandlerThread("RunningServices");
3996235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mBackgroundThread.start();
4006235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
4016235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mHandler.removeMessages(MSG_UPDATE_TIMES);
4026235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
4036235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
4046235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
4056235c6a83855fe2818affda3c82e1a245bd0232eIan Romanick        msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
406865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick        mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
407865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    }
40853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt
409865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    @Override
41053cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt    public Object onRetainNonConfigurationInstance() {
411865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick        return mState;
412865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    }
413865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick
414865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    @Override
415865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    protected void onSaveInstanceState(Bundle outState) {
416865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick        super.onSaveInstanceState(outState);
417865cf2d1f5e499916d360a246ad85554f3ff5b02Ian Romanick    }
41853cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt}
41953cdb7e51d85d4b4a35fba3ec200b27991b8488bEric Anholt