1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.Message;
23import android.os.RemoteException;
24import android.util.SparseArray;
25import java.util.List;
26
27/**
28 * Helper for monitoring the current importance of applications.
29 * @hide
30 */
31public class AppImportanceMonitor {
32    final Context mContext;
33
34    final SparseArray<AppEntry> mApps = new SparseArray<>();
35
36    static class AppEntry {
37        final int uid;
38        final SparseArray<Integer> procs = new SparseArray<>(1);
39        int importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
40
41        AppEntry(int _uid) {
42            uid = _uid;
43        }
44    }
45
46    final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
47        @Override
48        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
49        }
50
51        @Override
52        public void onProcessStateChanged(int pid, int uid, int procState) {
53            synchronized (mApps) {
54                updateImportanceLocked(pid, uid,
55                        ActivityManager.RunningAppProcessInfo.procStateToImportance(procState),
56                        true);
57            }
58        }
59
60        @Override
61        public void onProcessDied(int pid, int uid) {
62            synchronized (mApps) {
63                updateImportanceLocked(pid, uid,
64                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, true);
65            }
66        }
67    };
68
69    static final int MSG_UPDATE = 1;
70
71    final Handler mHandler;
72
73    public AppImportanceMonitor(Context context, Looper looper) {
74        mContext = context;
75        mHandler = new Handler(looper) {
76            @Override
77            public void handleMessage(Message msg) {
78                switch (msg.what) {
79                    case MSG_UPDATE:
80                        onImportanceChanged(msg.arg1, msg.arg2&0xffff, msg.arg2>>16);
81                        break;
82                    default:
83                        super.handleMessage(msg);
84                }
85            }
86        };
87        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
88        try {
89            ActivityManagerNative.getDefault().registerProcessObserver(mProcessObserver);
90        } catch (RemoteException e) {
91        }
92        List<ActivityManager.RunningAppProcessInfo> apps = am.getRunningAppProcesses();
93        if (apps != null) {
94            for (int i=0; i<apps.size(); i++) {
95                ActivityManager.RunningAppProcessInfo app = apps.get(i);
96                updateImportanceLocked(app.uid, app.pid, app.importance, false);
97            }
98        }
99    }
100
101    public int getImportance(int uid) {
102        AppEntry ent = mApps.get(uid);
103        if (ent == null) {
104            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
105        }
106        return ent.importance;
107    }
108
109    /**
110     * Report when an app's importance changed. Called on looper given to constructor.
111     */
112    public void onImportanceChanged(int uid, int importance, int oldImportance) {
113    }
114
115    void updateImportanceLocked(int uid, int pid, int importance, boolean repChange) {
116        AppEntry ent = mApps.get(uid);
117        if (ent == null) {
118            ent = new AppEntry(uid);
119            mApps.put(uid, ent);
120        }
121        if (importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
122            ent.procs.remove(pid);
123        } else {
124            ent.procs.put(pid, importance);
125        }
126        updateImportanceLocked(ent, repChange);
127    }
128
129    void updateImportanceLocked(AppEntry ent, boolean repChange) {
130        int appImp = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
131        for (int i=0; i<ent.procs.size(); i++) {
132            int procImp = ent.procs.valueAt(i);
133            if (procImp < appImp) {
134                appImp = procImp;
135            }
136        }
137        if (appImp != ent.importance) {
138            int impCode = appImp | (ent.importance<<16);
139            ent.importance = appImp;
140            if (appImp >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
141                mApps.remove(ent.uid);
142            }
143            if (repChange) {
144                mHandler.obtainMessage(MSG_UPDATE, ent.uid, impCode).sendToTarget();
145            }
146        }
147    }
148}
149