1/*
2 * Copyright (C) 2016 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.locksettings;
18
19import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
20import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
21import static com.android.internal.widget.LockPatternUtils.stringToPattern;
22
23import android.app.ActivityManager;
24import android.content.Context;
25import android.os.RemoteException;
26import android.os.ShellCommand;
27
28import com.android.internal.widget.LockPatternUtils;
29import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
30
31class LockSettingsShellCommand extends ShellCommand {
32
33    private static final String COMMAND_SET_PATTERN = "set-pattern";
34    private static final String COMMAND_SET_PIN = "set-pin";
35    private static final String COMMAND_SET_PASSWORD = "set-password";
36    private static final String COMMAND_CLEAR = "clear";
37    private static final String COMMAND_SP = "sp";
38    private static final String COMMAND_SET_DISABLED = "set-disabled";
39    private static final String COMMAND_VERIFY = "verify";
40    private static final String COMMAND_GET_DISABLED = "get-disabled";
41
42    private int mCurrentUserId;
43    private final LockPatternUtils mLockPatternUtils;
44    private final Context mContext;
45    private String mOld = "";
46    private String mNew = "";
47
48    LockSettingsShellCommand(Context context, LockPatternUtils lockPatternUtils) {
49        mContext = context;
50        mLockPatternUtils = lockPatternUtils;
51    }
52
53    @Override
54    public int onCommand(String cmd) {
55        try {
56            mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
57
58            parseArgs();
59            if (!checkCredential()) {
60                return -1;
61            }
62            switch (cmd) {
63                case COMMAND_SET_PATTERN:
64                    runSetPattern();
65                    break;
66                case COMMAND_SET_PASSWORD:
67                    runSetPassword();
68                    break;
69                case COMMAND_SET_PIN:
70                    runSetPin();
71                    break;
72                case COMMAND_CLEAR:
73                    runClear();
74                    break;
75                case COMMAND_SP:
76                    runChangeSp();
77                    break;
78                case COMMAND_SET_DISABLED:
79                    runSetDisabled();
80                    break;
81                case COMMAND_VERIFY:
82                    runVerify();
83                    break;
84                case COMMAND_GET_DISABLED:
85                    runGetDisabled();
86                    break;
87                default:
88                    getErrPrintWriter().println("Unknown command: " + cmd);
89                    break;
90            }
91            return 0;
92        } catch (Exception e) {
93            getErrPrintWriter().println("Error while executing command: " + cmd);
94            e.printStackTrace(getErrPrintWriter());
95            return -1;
96        }
97    }
98
99    private void runVerify() {
100        // The command is only run if the credential is correct.
101        getOutPrintWriter().println("Lock credential verified successfully");
102    }
103
104    @Override
105    public void onHelp() {
106    }
107
108    private void parseArgs() {
109        String opt;
110        while ((opt = getNextOption()) != null) {
111            if ("--old".equals(opt)) {
112                mOld = getNextArgRequired();
113            } else if ("--user".equals(opt)) {
114                mCurrentUserId = Integer.parseInt(getNextArgRequired());
115            } else {
116                getErrPrintWriter().println("Unknown option: " + opt);
117                throw new IllegalArgumentException();
118            }
119        }
120        mNew = getNextArg();
121    }
122
123    private void runChangeSp() {
124        if (mNew != null ) {
125            if ("1".equals(mNew)) {
126                mLockPatternUtils.enableSyntheticPassword();
127                getOutPrintWriter().println("Synthetic password enabled");
128            } else if ("0".equals(mNew)) {
129                mLockPatternUtils.disableSyntheticPassword();
130                getOutPrintWriter().println("Synthetic password disabled");
131            }
132        }
133        getOutPrintWriter().println(String.format("SP Enabled = %b",
134                mLockPatternUtils.isSyntheticPasswordEnabled()));
135    }
136
137    private void runSetPattern() throws RemoteException {
138        mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
139        getOutPrintWriter().println("Pattern set to '" + mNew + "'");
140    }
141
142    private void runSetPassword() throws RemoteException {
143        mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
144        getOutPrintWriter().println("Password set to '" + mNew + "'");
145    }
146
147    private void runSetPin() throws RemoteException {
148        mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
149        getOutPrintWriter().println("Pin set to '" + mNew + "'");
150    }
151
152    private void runClear() throws RemoteException {
153        mLockPatternUtils.clearLock(mOld, mCurrentUserId);
154        getOutPrintWriter().println("Lock credential cleared");
155    }
156
157    private void runSetDisabled() throws RemoteException {
158        final boolean disabled = Boolean.parseBoolean(mNew);
159        mLockPatternUtils.setLockScreenDisabled(disabled, mCurrentUserId);
160        getOutPrintWriter().println("Lock screen disabled set to " + disabled);
161    }
162
163    private void runGetDisabled() {
164        boolean isLockScreenDisabled = mLockPatternUtils.isLockScreenDisabled(mCurrentUserId);
165        getOutPrintWriter().println(isLockScreenDisabled);
166    }
167
168    private boolean checkCredential() throws RemoteException {
169        final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
170        final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
171        if (havePassword || havePattern) {
172            if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
173                getOutPrintWriter().println("Profile uses unified challenge");
174                return false;
175            }
176
177            try {
178                final boolean result;
179                if (havePassword) {
180                    result = mLockPatternUtils.checkPassword(mOld, mCurrentUserId);
181                } else {
182                    result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
183                }
184                if (!result) {
185                    if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
186                        mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
187                    }
188                    getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
189                }
190                return result;
191            } catch (RequestThrottledException e) {
192                getOutPrintWriter().println("Request throttled");
193                return false;
194            }
195        } else {
196            return true;
197        }
198    }
199}
200