1 /* 2 * Copyright (C) 2019 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 17 package com.android.server.testharness; 18 19 import android.annotation.Nullable; 20 import android.annotation.UserIdInt; 21 import android.app.KeyguardManager; 22 import android.app.Notification; 23 import android.app.NotificationManager; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.debug.AdbManagerInternal; 28 import android.location.LocationManager; 29 import android.os.BatteryManager; 30 import android.os.Binder; 31 import android.os.IBinder; 32 import android.os.ResultReceiver; 33 import android.os.ShellCallback; 34 import android.os.ShellCommand; 35 import android.os.SystemProperties; 36 import android.os.UserHandle; 37 import android.provider.Settings; 38 import android.util.Slog; 39 40 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 41 import com.android.internal.notification.SystemNotificationChannels; 42 import com.android.internal.widget.LockPatternUtils; 43 import com.android.server.LocalServices; 44 import com.android.server.PersistentDataBlockManagerInternal; 45 import com.android.server.SystemService; 46 import com.android.server.pm.UserManagerInternal; 47 48 import java.io.ByteArrayInputStream; 49 import java.io.ByteArrayOutputStream; 50 import java.io.DataInputStream; 51 import java.io.DataOutputStream; 52 import java.io.File; 53 import java.io.FileDescriptor; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.io.OutputStream; 57 import java.io.PrintWriter; 58 import java.nio.file.Files; 59 import java.nio.file.Path; 60 import java.nio.file.attribute.PosixFilePermission; 61 import java.util.Set; 62 63 /** 64 * Manages the Test Harness Mode service for setting up test harness mode on the device. 65 * 66 * <p>Test Harness Mode is a feature that allows the user to clean their device, retain ADB keys, 67 * and provision the device for Instrumentation testing. This means that all parts of the device 68 * that would otherwise interfere with testing (auto-syncing accounts, package verification, 69 * automatic updates, etc.) are all disabled by default but may be re-enabled by the user. 70 */ 71 public class TestHarnessModeService extends SystemService { 72 public static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness"; 73 private static final String TAG = TestHarnessModeService.class.getSimpleName(); 74 75 private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal; 76 TestHarnessModeService(Context context)77 public TestHarnessModeService(Context context) { 78 super(context); 79 } 80 81 @Override onStart()82 public void onStart() { 83 publishBinderService("testharness", mService); 84 } 85 86 @Override onBootPhase(int phase)87 public void onBootPhase(int phase) { 88 switch (phase) { 89 case PHASE_SYSTEM_SERVICES_READY: 90 setUpTestHarnessMode(); 91 break; 92 case PHASE_BOOT_COMPLETED: 93 completeTestHarnessModeSetup(); 94 showNotificationIfEnabled(); 95 break; 96 } 97 super.onBootPhase(phase); 98 } 99 100 /** 101 * Begin the setup for Test Harness Mode. 102 * 103 * <p>Note: This is just the things that <em>need</em> to be done before the device finishes 104 * booting for the first time. Everything else should be done after the system is done booting. 105 */ setUpTestHarnessMode()106 private void setUpTestHarnessMode() { 107 Slog.d(TAG, "Setting up test harness mode"); 108 byte[] testHarnessModeData = getTestHarnessModeData(); 109 if (testHarnessModeData == null) { 110 return; 111 } 112 // If there is data, we should set the device as provisioned, so that we skip the setup 113 // wizard. 114 setDeviceProvisioned(); 115 disableLockScreen(); 116 SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, "1"); 117 } 118 disableLockScreen()119 private void disableLockScreen() { 120 int mainUserId = getMainUserId(); 121 LockPatternUtils utils = new LockPatternUtils(getContext()); 122 utils.setLockScreenDisabled(true, mainUserId); 123 } 124 completeTestHarnessModeSetup()125 private void completeTestHarnessModeSetup() { 126 Slog.d(TAG, "Completing Test Harness Mode setup."); 127 byte[] testHarnessModeData = getTestHarnessModeData(); 128 if (testHarnessModeData == null) { 129 return; 130 } 131 try { 132 setUpAdbFiles(PersistentData.fromBytes(testHarnessModeData)); 133 configureSettings(); 134 configureUser(); 135 } catch (SetUpTestHarnessModeException e) { 136 Slog.e(TAG, "Failed to set up Test Harness Mode. Bad data.", e); 137 } finally { 138 // Clear out the Test Harness Mode data so that we don't repeat the setup. If it failed 139 // to set up, then retrying without enabling Test Harness Mode should allow it to boot. 140 // If we succeeded setting up, we shouldn't be re-applying the THM steps every boot 141 // anyway. 142 getPersistentDataBlock().clearTestHarnessModeData(); 143 } 144 } 145 getTestHarnessModeData()146 private byte[] getTestHarnessModeData() { 147 PersistentDataBlockManagerInternal blockManager = getPersistentDataBlock(); 148 if (blockManager == null) { 149 Slog.e(TAG, "Failed to start Test Harness Mode; no implementation of " 150 + "PersistentDataBlockManagerInternal was bound!"); 151 return null; 152 } 153 byte[] testHarnessModeData = blockManager.getTestHarnessModeData(); 154 if (testHarnessModeData == null || testHarnessModeData.length == 0) { 155 // There's no data to apply, so leave it as-is. 156 return null; 157 } 158 return testHarnessModeData; 159 } 160 configureSettings()161 private void configureSettings() { 162 ContentResolver cr = getContext().getContentResolver(); 163 164 // If adb is already enabled, then we need to restart the daemon to pick up the change in 165 // keys. This is only really useful for userdebug/eng builds. 166 if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1) { 167 SystemProperties.set("ctl.restart", "adbd"); 168 Slog.d(TAG, "Restarted adbd"); 169 } 170 171 // Disable the TTL for ADB keys before ADB is enabled as a part of AdbService's 172 // initialization. 173 Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0); 174 Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); 175 Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 0); 176 Settings.Global.putInt( 177 cr, 178 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 179 BatteryManager.BATTERY_PLUGGED_ANY); 180 Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1); 181 } 182 setUpAdbFiles(PersistentData persistentData)183 private void setUpAdbFiles(PersistentData persistentData) { 184 AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class); 185 186 if (adbManager.getAdbKeysFile() != null) { 187 writeBytesToFile(persistentData.mAdbKeys, adbManager.getAdbKeysFile().toPath()); 188 } 189 if (adbManager.getAdbTempKeysFile() != null) { 190 writeBytesToFile(persistentData.mAdbTempKeys, adbManager.getAdbTempKeysFile().toPath()); 191 } 192 adbManager.notifyKeyFilesUpdated(); 193 } 194 configureUser()195 private void configureUser() { 196 int mainUserId = getMainUserId(); 197 198 ContentResolver.setMasterSyncAutomaticallyAsUser(false, mainUserId); 199 200 LocationManager locationManager = getContext().getSystemService(LocationManager.class); 201 locationManager.setLocationEnabledForUser(true, UserHandle.of(mainUserId)); 202 } 203 getMainUserId()204 private @UserIdInt int getMainUserId() { 205 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); 206 int mainUserId = umi.getMainUserId(); 207 if (mainUserId >= 0) { 208 return mainUserId; 209 } else { 210 // If there is no MainUser, fall back to the historical usage of user 0. 211 Slog.w(TAG, "No MainUser exists; using user 0 instead"); 212 return UserHandle.USER_SYSTEM; 213 } 214 } 215 writeBytesToFile(byte[] keys, Path adbKeys)216 private void writeBytesToFile(byte[] keys, Path adbKeys) { 217 try { 218 OutputStream fileOutputStream = Files.newOutputStream(adbKeys); 219 fileOutputStream.write(keys); 220 fileOutputStream.close(); 221 222 Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(adbKeys); 223 permissions.add(PosixFilePermission.GROUP_READ); 224 Files.setPosixFilePermissions(adbKeys, permissions); 225 } catch (IOException e) { 226 Slog.e(TAG, "Failed to set up adb keys", e); 227 // Note: if a device enters this block, it will remain UNAUTHORIZED in ADB, but all 228 // other settings will be set up. 229 } 230 } 231 232 // Setting the device as provisioned skips the setup wizard. setDeviceProvisioned()233 private void setDeviceProvisioned() { 234 ContentResolver cr = getContext().getContentResolver(); 235 Settings.Global.putInt(cr, Settings.Global.DEVICE_PROVISIONED, 1); 236 Settings.Secure.putIntForUser( 237 cr, 238 Settings.Secure.USER_SETUP_COMPLETE, 239 1, 240 UserHandle.USER_CURRENT); 241 } 242 showNotificationIfEnabled()243 private void showNotificationIfEnabled() { 244 if (!SystemProperties.getBoolean(TEST_HARNESS_MODE_PROPERTY, false)) { 245 return; 246 } 247 String title = getContext() 248 .getString(com.android.internal.R.string.test_harness_mode_notification_title); 249 String message = getContext() 250 .getString(com.android.internal.R.string.test_harness_mode_notification_message); 251 252 Notification notification = 253 new Notification.Builder(getContext(), SystemNotificationChannels.DEVELOPER) 254 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) 255 .setWhen(0) 256 .setOngoing(true) 257 .setTicker(title) 258 .setDefaults(0) // please be quiet 259 .setColor(getContext().getColor( 260 com.android.internal.R.color 261 .system_notification_accent_color)) 262 .setContentTitle(title) 263 .setContentText(message) 264 .setVisibility(Notification.VISIBILITY_PUBLIC) 265 .build(); 266 267 NotificationManager notificationManager = 268 getContext().getSystemService(NotificationManager.class); 269 notificationManager.notifyAsUser( 270 null, SystemMessage.NOTE_TEST_HARNESS_MODE_ENABLED, notification, UserHandle.ALL); 271 } 272 273 @Nullable getPersistentDataBlock()274 private PersistentDataBlockManagerInternal getPersistentDataBlock() { 275 if (mPersistentDataBlockManagerInternal == null) { 276 Slog.d(TAG, "Getting PersistentDataBlockManagerInternal from LocalServices"); 277 mPersistentDataBlockManagerInternal = 278 LocalServices.getService(PersistentDataBlockManagerInternal.class); 279 } 280 return mPersistentDataBlockManagerInternal; 281 } 282 283 private final IBinder mService = new Binder() { 284 @Override 285 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 286 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 287 (new TestHarnessModeShellCommand()) 288 .exec(this, in, out, err, args, callback, resultReceiver); 289 } 290 }; 291 292 private class TestHarnessModeShellCommand extends ShellCommand { 293 @Override onCommand(String cmd)294 public int onCommand(String cmd) { 295 if (cmd == null) { 296 return handleDefaultCommands(cmd); 297 } 298 switch (cmd) { 299 case "enable": 300 case "restore": 301 checkPermissions(); 302 final long originalId = Binder.clearCallingIdentity(); 303 try { 304 if (isDeviceSecure()) { 305 getErrPrintWriter().println( 306 "Test Harness Mode cannot be enabled if there is a lock " 307 + "screen"); 308 return 2; 309 } 310 return handleEnable(); 311 } finally { 312 Binder.restoreCallingIdentity(originalId); 313 } 314 default: 315 return handleDefaultCommands(cmd); 316 } 317 } 318 checkPermissions()319 private void checkPermissions() { 320 getContext().enforceCallingPermission( 321 android.Manifest.permission.ENABLE_TEST_HARNESS_MODE, 322 "You must hold android.permission.ENABLE_TEST_HARNESS_MODE " 323 + "to enable Test Harness Mode"); 324 } 325 isDeviceSecure()326 private boolean isDeviceSecure() { 327 KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class); 328 return keyguardManager.isDeviceSecure(getMainUserId()); 329 } 330 handleEnable()331 private int handleEnable() { 332 AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class); 333 File adbKeys = adbManager.getAdbKeysFile(); 334 File adbTempKeys = adbManager.getAdbTempKeysFile(); 335 336 try { 337 byte[] adbKeysBytes = getBytesFromFile(adbKeys); 338 byte[] adbTempKeysBytes = getBytesFromFile(adbTempKeys); 339 340 PersistentData persistentData = new PersistentData(adbKeysBytes, adbTempKeysBytes); 341 PersistentDataBlockManagerInternal blockManager = getPersistentDataBlock(); 342 if (blockManager == null) { 343 Slog.e(TAG, "Failed to enable Test Harness Mode. No implementation of " 344 + "PersistentDataBlockManagerInternal was bound."); 345 getErrPrintWriter().println("Failed to enable Test Harness Mode"); 346 return 1; 347 } 348 blockManager.setTestHarnessModeData(persistentData.toBytes()); 349 } catch (IOException e) { 350 Slog.e(TAG, "Failed to store ADB keys.", e); 351 getErrPrintWriter().println("Failed to enable Test Harness Mode"); 352 return 1; 353 } 354 355 Intent i = new Intent(Intent.ACTION_FACTORY_RESET); 356 i.setPackage("android"); 357 i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 358 i.putExtra(Intent.EXTRA_REASON, TAG); 359 i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true); 360 getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM); 361 return 0; 362 } 363 getBytesFromFile(File file)364 private byte[] getBytesFromFile(File file) throws IOException { 365 if (file == null || !file.exists()) { 366 return new byte[0]; 367 } 368 Path path = file.toPath(); 369 try (InputStream inputStream = Files.newInputStream(path)) { 370 int size = (int) Files.size(path); 371 byte[] bytes = new byte[size]; 372 int numBytes = inputStream.read(bytes); 373 if (numBytes != size) { 374 throw new IOException("Failed to read the whole file"); 375 } 376 return bytes; 377 } 378 } 379 380 @Override onHelp()381 public void onHelp() { 382 PrintWriter pw = getOutPrintWriter(); 383 pw.println("About:"); 384 pw.println(" Test Harness Mode is a mode that the device can be placed in to prepare"); 385 pw.println(" the device for running UI tests. The device is placed into this mode by"); 386 pw.println(" first wiping all data from the device, preserving ADB keys."); 387 pw.println(); 388 pw.println(" By default, the following settings are configured:"); 389 pw.println(" * Package Verifier is disabled"); 390 pw.println(" * Stay Awake While Charging is enabled"); 391 pw.println(" * OTA Updates are disabled"); 392 pw.println(" * Auto-Sync for accounts is disabled"); 393 pw.println(); 394 pw.println(" Other apps may configure themselves differently in Test Harness Mode by"); 395 pw.println(" checking ActivityManager.isRunningInUserTestHarness()"); 396 pw.println(); 397 pw.println("Test Harness Mode commands:"); 398 pw.println(" help"); 399 pw.println(" Print this help text."); 400 pw.println(); 401 pw.println(" enable|restore"); 402 pw.println(" Erase all data from this device and enable Test Harness Mode,"); 403 pw.println(" preserving the stored ADB keys currently on the device and toggling"); 404 pw.println(" settings in a way that are conducive to Instrumentation testing."); 405 } 406 } 407 408 /** 409 * The object that will serialize/deserialize the Test Harness Mode data to and from the 410 * persistent data block. 411 */ 412 public static class PersistentData { 413 static final byte VERSION_1 = 1; 414 static final byte VERSION_2 = 2; 415 416 final int mVersion; 417 final byte[] mAdbKeys; 418 final byte[] mAdbTempKeys; 419 PersistentData(byte[] adbKeys, byte[] adbTempKeys)420 PersistentData(byte[] adbKeys, byte[] adbTempKeys) { 421 this(VERSION_2, adbKeys, adbTempKeys); 422 } 423 PersistentData(int version, byte[] adbKeys, byte[] adbTempKeys)424 PersistentData(int version, byte[] adbKeys, byte[] adbTempKeys) { 425 this.mVersion = version; 426 this.mAdbKeys = adbKeys; 427 this.mAdbTempKeys = adbTempKeys; 428 } 429 fromBytes(byte[] bytes)430 static PersistentData fromBytes(byte[] bytes) throws SetUpTestHarnessModeException { 431 try { 432 DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes)); 433 int version = is.readInt(); 434 if (version == VERSION_1) { 435 // Version 1 of Test Harness Mode contained an "enabled" bit that we need to 436 // skip. If we don't, the binary format will be bad and it will fail to set up. 437 is.readBoolean(); 438 } 439 int adbKeysLength = is.readInt(); 440 byte[] adbKeys = new byte[adbKeysLength]; 441 is.readFully(adbKeys); 442 int adbTempKeysLength = is.readInt(); 443 byte[] adbTempKeys = new byte[adbTempKeysLength]; 444 is.readFully(adbTempKeys); 445 return new PersistentData(version, adbKeys, adbTempKeys); 446 } catch (IOException e) { 447 throw new SetUpTestHarnessModeException(e); 448 } 449 } 450 toBytes()451 byte[] toBytes() { 452 try { 453 ByteArrayOutputStream os = new ByteArrayOutputStream(); 454 DataOutputStream dos = new DataOutputStream(os); 455 dos.writeInt(VERSION_2); 456 dos.writeInt(mAdbKeys.length); 457 dos.write(mAdbKeys); 458 dos.writeInt(mAdbTempKeys.length); 459 dos.write(mAdbTempKeys); 460 dos.close(); 461 return os.toByteArray(); 462 } catch (IOException e) { 463 throw new RuntimeException(e); 464 } 465 } 466 } 467 468 /** 469 * An exception thrown when Test Harness Mode fails to set up. 470 * 471 * <p>In the event that Test Harness Mode fails to set up, all of the data should be discarded 472 * and the Test Harness Mode portion of the persistent data block should be wiped. This will 473 * prevent the device from becoming stuck, as there is no way (without rooting the device) to 474 * clear the persistent data block. 475 */ 476 private static class SetUpTestHarnessModeException extends Exception { SetUpTestHarnessModeException(Exception e)477 SetUpTestHarnessModeException(Exception e) { 478 super(e); 479 } 480 } 481 } 482