1/*
2 * Copyright (C) 2015 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 com.android.server;
18
19import com.android.internal.widget.LockPatternUtils;
20import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
21
22import android.app.trust.IStrongAuthTracker;
23import android.content.Context;
24import android.os.DeadObjectException;
25import android.os.Handler;
26import android.os.Message;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.util.Slog;
30import android.util.SparseIntArray;
31
32import java.util.ArrayList;
33
34import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
35
36/**
37 * Keeps track of requests for strong authentication.
38 */
39public class LockSettingsStrongAuth {
40
41    private static final String TAG = "LockSettings";
42
43    private static final int MSG_REQUIRE_STRONG_AUTH = 1;
44    private static final int MSG_REGISTER_TRACKER = 2;
45    private static final int MSG_UNREGISTER_TRACKER = 3;
46    private static final int MSG_REMOVE_USER = 4;
47
48    private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
49    private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
50    private final int mDefaultStrongAuthFlags;
51
52    public LockSettingsStrongAuth(Context context) {
53        mDefaultStrongAuthFlags = StrongAuthTracker.getDefaultFlags(context);
54    }
55
56    private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
57        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
58            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
59                return;
60            }
61        }
62        mStrongAuthTrackers.add(tracker);
63
64        for (int i = 0; i < mStrongAuthForUser.size(); i++) {
65            int key = mStrongAuthForUser.keyAt(i);
66            int value = mStrongAuthForUser.valueAt(i);
67            try {
68                tracker.onStrongAuthRequiredChanged(value, key);
69            } catch (RemoteException e) {
70                Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
71            }
72        }
73    }
74
75    private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
76        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
77            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
78                mStrongAuthTrackers.remove(i);
79                return;
80            }
81        }
82    }
83
84    private void handleRequireStrongAuth(int strongAuthReason, int userId) {
85        if (userId == UserHandle.USER_ALL) {
86            for (int i = 0; i < mStrongAuthForUser.size(); i++) {
87                int key = mStrongAuthForUser.keyAt(i);
88                handleRequireStrongAuthOneUser(strongAuthReason, key);
89            }
90        } else {
91            handleRequireStrongAuthOneUser(strongAuthReason, userId);
92        }
93    }
94
95    private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) {
96        int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags);
97        int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED
98                ? STRONG_AUTH_NOT_REQUIRED
99                : (oldValue | strongAuthReason);
100        if (oldValue != newValue) {
101            mStrongAuthForUser.put(userId, newValue);
102            notifyStrongAuthTrackers(newValue, userId);
103        }
104    }
105
106    private void handleRemoveUser(int userId) {
107        int index = mStrongAuthForUser.indexOfKey(userId);
108        if (index >= 0) {
109            mStrongAuthForUser.removeAt(index);
110            notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId);
111        }
112    }
113
114    private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
115        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
116            try {
117                mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
118            } catch (DeadObjectException e) {
119                Slog.d(TAG, "Removing dead StrongAuthTracker.");
120                mStrongAuthTrackers.remove(i);
121                i--;
122            } catch (RemoteException e) {
123                Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
124            }
125        }
126    }
127
128    public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
129        mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
130    }
131
132    public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
133        mHandler.obtainMessage(MSG_UNREGISTER_TRACKER, tracker).sendToTarget();
134    }
135
136    public void removeUser(int userId) {
137        mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
138    }
139
140    public void requireStrongAuth(int strongAuthReason, int userId) {
141        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
142            mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
143                    userId).sendToTarget();
144        } else {
145            throw new IllegalArgumentException(
146                    "userId must be an explicit user id or USER_ALL");
147        }
148    }
149
150    public void reportUnlock(int userId) {
151        requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
152    }
153
154    private final Handler mHandler = new Handler() {
155        @Override
156        public void handleMessage(Message msg) {
157            switch (msg.what) {
158                case MSG_REGISTER_TRACKER:
159                    handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
160                    break;
161                case MSG_UNREGISTER_TRACKER:
162                    handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj);
163                    break;
164                case MSG_REQUIRE_STRONG_AUTH:
165                    handleRequireStrongAuth(msg.arg1, msg.arg2);
166                    break;
167                case MSG_REMOVE_USER:
168                    handleRemoveUser(msg.arg1);
169                    break;
170            }
171        }
172    };
173}
174