Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c01b0c83 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Allow user to see and stop heavy-weight processes.

Change-Id: If5caed3972ab03a54fbf8c459cdfc136e4bdc020
parent 12fd447d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1788,6 +1788,9 @@ found in the list of installed applications.</string>
    <!-- Running service details, default description for services that are started. -->
    <string name="service_stop_description">This service was started by its
        application.  Stopping it may cause the application to fail.</string>
    <!-- Running service details, description for running heavy-weight process. -->
    <string name="heavy_weight_stop_description">This application can not safely
        be stopped.  Doing so may lose some of your current work.</string>
    <!-- Running service details, default description for services that are managed. -->
    <string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
        currently in use.  Touch Settings to control it.</string>
+10 −2
Original line number Diff line number Diff line
@@ -130,8 +130,16 @@ public class RunningProcessesView extends FrameLayout
                    uptimeView.setText(DateUtils.formatElapsedTime(builder,
                            (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
                } else {
                    boolean isService = false;
                    if (mItem instanceof RunningState.MergedItem) {
                        isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
                    }
                    if (isService) {
                        uptimeView.setText(context.getResources().getText(
                                R.string.service_restarting));
                    } else {
                        uptimeView.setText("");
                    }
                }
            }
        }
+141 −113
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ public class RunningServiceDetails extends Activity {
    RunningProcessesView.ActiveItem mSnippetActiveItem;
    RunningProcessesView.ViewHolder mSnippetViewHolder;
    
    int mNumServices, mNumProcesses;
    
    TextView mServicesHeader;
    TextView mProcessesHeader;
    final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
@@ -79,7 +81,7 @@ public class RunningServiceDetails extends Activity {
                } catch (ActivityNotFoundException e) {
                    Log.w(TAG, e);
                }
            } else {
            } else if (mActiveItem.mItem instanceof RunningState.ServiceItem) {
                RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
                stopService(new Intent().setComponent(si.mRunningService.service));
                if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
@@ -91,6 +93,10 @@ public class RunningServiceDetails extends Activity {
                        mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
                    }
                }
            } else {
                // Heavy-weight process.  We'll do a force-stop on it.
                mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
                finish();
            }
        }
    }
@@ -163,46 +169,32 @@ public class RunningServiceDetails extends Activity {
        return false;
    }
    
    void addDetailViews() {
        for (int i=mActiveDetails.size()-1; i>=0; i--) {
            mAllDetails.removeView(mActiveDetails.get(i).mRootView);
        }
        mActiveDetails.clear();
        
        if (mServicesHeader != null) {
            mAllDetails.removeView(mServicesHeader);
            mServicesHeader = null;
        }
        
        if (mProcessesHeader != null) {
            mAllDetails.removeView(mProcessesHeader);
            mProcessesHeader = null;
        }
        
        if (mMergedItem != null) {
            for (int i=0; i<mMergedItem.mServices.size(); i++) {
                if (i == 0) {
    void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) {
        if (mNumServices == 0) {
            mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
                    mAllDetails, false);
            mServicesHeader.setText(R.string.runningservicedetails_services_title);
            mAllDetails.addView(mServicesHeader);
        }
                RunningState.ServiceItem si = mMergedItem.mServices.get(i);
        mNumServices++;
        
        RunningState.BaseItem bi = si != null ? si : mi;
        
        ActiveDetail detail = new ActiveDetail();
        View root = mInflater.inflate(R.layout.running_service_details_service,
                mAllDetails, false);
        mAllDetails.addView(root);
        detail.mRootView = root;
        detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
                detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
        detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder);
        
                if (si.mRunningService.clientLabel != 0) {
        if (si != null && si.mRunningService.clientLabel != 0) {
            detail.mManageIntent = mAm.getRunningServiceControlPanel(
                    si.mRunningService.service);
        }
        
        TextView description = (TextView)root.findViewById(R.id.comp_description);
                if (si.mServiceInfo.descriptionRes != 0) {
        if (si != null && si.mServiceInfo.descriptionRes != 0) {
            description.setText(getPackageManager().getText(
                    si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
                    si.mServiceInfo.applicationInfo));
@@ -217,7 +209,9 @@ public class RunningServiceDetails extends Activity {
                } catch (PackageManager.NameNotFoundException e) {
                }
            } else {
                        description.setText(getText(R.string.service_stop_description));
                description.setText(getText(si != null
                        ? R.string.service_stop_description
                        : R.string.heavy_weight_stop_description));
            }
        }
        
@@ -230,21 +224,15 @@ public class RunningServiceDetails extends Activity {
        mActiveDetails.add(detail);
    }
    
            boolean didProcess = false;
            for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
                RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
                        : mMergedItem.mOtherProcesses.get(i);
                if (pi.mPid <= 0) {
                    continue;
                }
                
                if (!didProcess) {
    void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) {
        if (mNumProcesses == 0) {
            mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
                    mAllDetails, false);
            mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
            mAllDetails.addView(mProcessesHeader);
                    didProcess = true;
        }
        mNumProcesses++;
        
        ActiveDetail detail = new ActiveDetail();
        View root = mInflater.inflate(R.layout.running_service_details_process,
                mAllDetails, false);
@@ -254,7 +242,7 @@ public class RunningServiceDetails extends Activity {
        detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
        
        TextView description = (TextView)root.findViewById(R.id.comp_description);
                if (i < 0) {
        if (isMain) {
            description.setText(R.string.main_running_process_description);
        } else {
            int textid = 0;
@@ -274,7 +262,7 @@ public class RunningServiceDetails extends Activity {
                    }
                    if (providers != null) {
                        for (int j=0; j<providers.size(); j++) {
                                    ProviderInfo prov = providers.get(i);
                            ProviderInfo prov = providers.get(j);
                            if (comp.getClassName().equals(prov.name)) {
                                label = RunningState.makeLabel(getPackageManager(),
                                        prov.name, prov);
@@ -303,6 +291,46 @@ public class RunningServiceDetails extends Activity {
        
        mActiveDetails.add(detail);
    }
    
    void addDetailViews() {
        for (int i=mActiveDetails.size()-1; i>=0; i--) {
            mAllDetails.removeView(mActiveDetails.get(i).mRootView);
        }
        mActiveDetails.clear();
        
        if (mServicesHeader != null) {
            mAllDetails.removeView(mServicesHeader);
            mServicesHeader = null;
        }
        
        if (mProcessesHeader != null) {
            mAllDetails.removeView(mProcessesHeader);
            mProcessesHeader = null;
        }
        
        mNumServices = mNumProcesses = 0;
        
        if (mMergedItem != null) {
            for (int i=0; i<mMergedItem.mServices.size(); i++) {
                addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem);
            }
            
            if (mMergedItem.mServices.size() <= 0) {
                // This item does not have any services, so it must be
                // a heavy-weight process...  we will put a fake service
                // entry for it, to allow the user to "stop" it.
                addServiceDetailsView(null, mMergedItem);
            }
            
            for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
                RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
                        : mMergedItem.mOtherProcesses.get(i);
                if (pi.mPid <= 0) {
                    continue;
                }
                
                addProcessDetailsView(pi, i < 0);
            }
        }
    }
    
+78 −19
Original line number Diff line number Diff line
@@ -47,18 +47,34 @@ import java.util.List;
 */
public class RunningState {

    final SparseArray<HashMap<String, ProcessItem>> mProcesses
    // Processes that are hosting a service we are interested in, organized
    // by uid and name.  Note that this mapping does not change even across
    // service restarts, and during a restart there will still be a process
    // entry.
    final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName
            = new SparseArray<HashMap<String, ProcessItem>>();
    final SparseArray<ProcessItem> mActiveProcesses
    
    // Processes that are hosting a service we are interested in, organized
    // by their pid.  These disappear and re-appear as services are restarted.
    final SparseArray<ProcessItem> mServiceProcessesByPid
            = new SparseArray<ProcessItem>();
    
    // Used to sort the interesting processes.
    final ServiceProcessComparator mServiceProcessComparator
            = new ServiceProcessComparator();
    
    // Temporary for finding process dependencies.
    // Additional heavy-weight processes to be shown to the user, even if
    // there is no service running in them.
    final ArrayList<ProcessItem> mHeavyProcesses = new ArrayList<ProcessItem>();
    
    // All currently running processes, for finding dependencies etc.
    final SparseArray<ProcessItem> mRunningProcesses
            = new SparseArray<ProcessItem>();
    
    // The processes associated with services, in sorted order.
    final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
    
    // All processes, used for retrieving memory information.
    final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
    
    int mSequence = 0;
@@ -128,6 +144,8 @@ public class RunningState {
        int mRunningSeq;
        ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
        
        MergedItem mMergedItem;
        
        // Purely for sorting.
        boolean mIsSystem;
        boolean mIsStarted;
@@ -435,10 +453,10 @@ public class RunningState {
                continue;
            }
            
            HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
            HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid);
            if (procs == null) {
                procs = new HashMap<String, ProcessItem>();
                mProcesses.put(si.uid, procs);
                mServiceProcessesByName.put(si.uid, procs);
            }
            ProcessItem proc = procs.get(si.process);
            if (proc == null) {
@@ -453,10 +471,10 @@ public class RunningState {
                    changed = true;
                    if (proc.mPid != pid) {
                        if (proc.mPid != 0) {
                            mActiveProcesses.remove(proc.mPid);
                            mServiceProcessesByPid.remove(proc.mPid);
                        }
                        if (pid != 0) {
                            mActiveProcesses.put(pid, proc);
                            mServiceProcessesByPid.put(pid, proc);
                        }
                        proc.mPid = pid;
                    }
@@ -474,19 +492,30 @@ public class RunningState {
        final int NP = processes != null ? processes.size() : 0;
        for (int i=0; i<NP; i++) {
            ActivityManager.RunningAppProcessInfo pi = processes.get(i);
            ProcessItem proc = mActiveProcesses.get(pi.pid);
            ProcessItem proc = mServiceProcessesByPid.get(pi.pid);
            if (proc == null) {
                // This process is not one that is a direct container
                // of a service, so look for it in the secondary
                // running list.
                proc = mRunningProcesses.get(pi.pid);
                if (proc == null) {
                    changed = true;
                    proc = new ProcessItem(context, pi.uid, pi.processName);
                    proc.mPid = pi.pid;
                    mRunningProcesses.put(pi.pid, proc);
                }
                proc.mDependentProcesses.clear();
            }
            
            if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_HEAVY_WEIGHT) != 0) {
                if (!mHeavyProcesses.contains(proc)) {
                    changed = true;
                    mHeavyProcesses.add(proc);
                }
                proc.mCurSeq = mSequence;
                proc.ensureLabel(pm);
            }
            
            proc.mRunningSeq = mSequence;
            proc.mRunningProcessInfo = pi;
        }
@@ -499,7 +528,7 @@ public class RunningState {
            if (proc.mRunningSeq == mSequence) {
                int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
                if (clientPid != 0) {
                    ProcessItem client = mActiveProcesses.get(clientPid);
                    ProcessItem client = mServiceProcessesByPid.get(clientPid);
                    if (client == null) {
                        client = mRunningProcesses.get(clientPid);
                    }
@@ -508,29 +537,42 @@ public class RunningState {
                    }
                } else {
                    // In this pass the process doesn't have a client.
                    // Clear to make sure if it later gets the same one
                    // that we will detect the change.
                    // Clear to make sure that, if it later gets the same one,
                    // we will detect the change.
                    proc.mClient = null;
                }
            } else {
                changed = true;
                mRunningProcesses.remove(mRunningProcesses.keyAt(i));
            }
        }
        
        // Remove any old heavy processes.
        int NHP = mHeavyProcesses.size();
        for (int i=0; i<NHP; i++) {
            ProcessItem proc = mHeavyProcesses.get(i);
            if (mRunningProcesses.get(proc.mPid) == null) {
                changed = true;
                mHeavyProcesses.remove(i);
                i--;
                NHP--;
            }
        }
        
        // Follow the tree from all primary service processes to all
        // processes they are dependent on, marking these processes as
        // still being active and determining if anything has changed.
        final int NAP = mActiveProcesses.size();
        final int NAP = mServiceProcessesByPid.size();
        for (int i=0; i<NAP; i++) {
            ProcessItem proc = mActiveProcesses.valueAt(i);
            ProcessItem proc = mServiceProcessesByPid.valueAt(i);
            if (proc.mCurSeq == mSequence) {
                changed |= proc.buildDependencyChain(context, pm, mSequence);
            }
        }
        
        // Look for services and their primary processes that no longer exist...
        for (int i=0; i<mProcesses.size(); i++) {
            HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
        for (int i=0; i<mServiceProcessesByName.size(); i++) {
            HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i);
            Iterator<ProcessItem> pit = procs.values().iterator();
            while (pit.hasNext()) {
                ProcessItem pi = pit.next();
@@ -545,10 +587,10 @@ public class RunningState {
                    changed = true;
                    pit.remove();
                    if (procs.size() == 0) {
                        mProcesses.remove(mProcesses.keyAt(i));
                        mServiceProcessesByName.remove(mServiceProcessesByName.keyAt(i));
                    }
                    if (pi.mPid != 0) {
                        mActiveProcesses.remove(pi.mPid);
                        mServiceProcessesByPid.remove(pi.mPid);
                    }
                    continue;
                }
@@ -566,8 +608,8 @@ public class RunningState {
        if (changed) {
            // First determine an order for the services.
            ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
            for (int i=0; i<mProcesses.size(); i++) {
                for (ProcessItem pi : mProcesses.valueAt(i).values()) {
            for (int i=0; i<mServiceProcessesByName.size(); i++) {
                for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) {
                    pi.mIsSystem = false;
                    pi.mIsStarted = true;
                    pi.mActiveSince = Long.MAX_VALUE;
@@ -643,6 +685,23 @@ public class RunningState {
                mergedItem.update(context);
                newMergedItems.add(mergedItem);
            }
            
            // Finally, heavy-weight processes need to be shown and will
            // go at the top.
            NHP = mHeavyProcesses.size();
            for (int i=0; i<NHP; i++) {
                ProcessItem proc = mHeavyProcesses.get(i);
                if (proc.mClient == null && proc.mServices.size() <= 0) {
                    if (proc.mMergedItem == null) {
                        proc.mMergedItem = new MergedItem();
                        proc.mMergedItem.mProcess = proc;
                    }
                    proc.mMergedItem.update(context);
                    newMergedItems.add(0, proc.mMergedItem);
                    mProcessItems.add(proc);
                }
            }
            
            synchronized (mLock) {
                mItems = newItems;
                mMergedItems = newMergedItems;
+10 −14
Original line number Diff line number Diff line
@@ -288,9 +288,9 @@ public class BatteryHistoryChart extends View {
    }

    void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
            int lastBatX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
            int lastX, boolean lastCharging, boolean lastScreenOn, Path lastPath) {
        if (curLevelPath != null) {
            if (lastBatX >= 0) {
            if (lastX >= 0 && lastX < w) {
                if (lastPath != null) {
                    lastPath.lineTo(w, y);
                }
@@ -301,10 +301,10 @@ public class BatteryHistoryChart extends View {
            curLevelPath.close();
        }
        
        if (lastCharging) {
        if (lastCharging && lastX < w) {
            mChargingPath.lineTo(w, h-mChargingOffset);
        }
        if (lastScreenOn) {
        if (lastScreenOn && lastX < w) {
            mScreenOnPath.lineTo(w, h-mScreenOnOffset);
        }
    }
@@ -329,7 +329,7 @@ public class BatteryHistoryChart extends View {
        final int levelh = h - mLevelOffset;
        
        BatteryStats.HistoryItem rec = mHistFirst;
        int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1, lastBatX = -1;
        int x = 0, y = 0, startX = 0, lastX = -1, lastY = -1;
        int i = 0;
        Path curLevelPath = null;
        Path lastLinePath = null;
@@ -342,11 +342,8 @@ public class BatteryHistoryChart extends View {
                
                if (lastX != x) {
                    // We have moved by at least a pixel.
                    if (lastY == y) {
                        // Battery level is still the same; don't plot,
                        // but remember it.
                        lastBatX = x;
                    } else {
                    if (lastY != y) {
                        // Don't plot changes within a pixel.
                        Path path;
                        byte value = rec.batteryLevel;
                        if (value <= BATTERY_CRITICAL) path = mBatCriticalPath;
@@ -372,7 +369,6 @@ public class BatteryHistoryChart extends View {
                        }
                        lastX = x;
                        lastY = y;
                        lastBatX = -1;
                        
                        final boolean charging =
                            (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
@@ -399,9 +395,9 @@ public class BatteryHistoryChart extends View {
                }
                
            } else if (curLevelPath != null) {
                finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastBatX,
                finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
                        lastCharging, lastScreenOn, lastLinePath);
                lastX = lastY = lastBatX = -1;
                lastX = lastY = -1;
                curLevelPath = null;
                lastLinePath = null;
                lastCharging = lastScreenOn = false;
@@ -411,7 +407,7 @@ public class BatteryHistoryChart extends View {
            i++;
        }
        
        finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastBatX,
        finishPaths(w, h, levelh, startX, lastY, curLevelPath, lastX,
                lastCharging, lastScreenOn, lastLinePath);
    }