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 17 package com.android.server.retaildemo; 18 19 import android.Manifest; 20 import android.app.ActivityManagerInternal; 21 import android.app.ActivityManagerNative; 22 import android.app.AppGlobals; 23 import android.app.Notification; 24 import android.app.NotificationManager; 25 import android.app.PendingIntent; 26 import android.app.RetailDemoModeServiceInternal; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.ContentProvider; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.DialogInterface; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.IPackageManager; 36 import android.content.pm.PackageManager; 37 import android.content.pm.ResolveInfo; 38 import android.content.pm.UserInfo; 39 import android.content.res.Configuration; 40 import android.database.ContentObserver; 41 import android.hardware.camera2.CameraAccessException; 42 import android.hardware.camera2.CameraCharacteristics; 43 import android.hardware.camera2.CameraManager; 44 import android.media.AudioManager; 45 import android.media.AudioSystem; 46 import android.net.Uri; 47 import android.os.Environment; 48 import android.os.FileUtils; 49 import android.os.Handler; 50 import android.os.Looper; 51 import android.os.Message; 52 import android.os.PowerManager; 53 import android.os.RemoteException; 54 import android.os.SystemClock; 55 import android.os.SystemProperties; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.provider.CallLog; 59 import android.provider.MediaStore; 60 import android.provider.Settings; 61 import android.util.KeyValueListParser; 62 import android.util.Slog; 63 import com.android.internal.os.BackgroundThread; 64 import com.android.internal.R; 65 import com.android.internal.annotations.GuardedBy; 66 import com.android.internal.logging.MetricsLogger; 67 import com.android.internal.widget.LockPatternUtils; 68 import com.android.server.LocalServices; 69 import com.android.server.ServiceThread; 70 import com.android.server.SystemService; 71 import com.android.server.am.ActivityManagerService; 72 import com.android.server.retaildemo.UserInactivityCountdownDialog.OnCountDownExpiredListener; 73 74 import java.io.File; 75 import java.util.ArrayList; 76 77 public class RetailDemoModeService extends SystemService { 78 private static final boolean DEBUG = false; 79 80 private static final String TAG = RetailDemoModeService.class.getSimpleName(); 81 private static final String DEMO_USER_NAME = "Demo"; 82 private static final String ACTION_RESET_DEMO = 83 "com.android.server.retaildemo.ACTION_RESET_DEMO"; 84 private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled"; 85 86 private static final int MSG_TURN_SCREEN_ON = 0; 87 private static final int MSG_INACTIVITY_TIME_OUT = 1; 88 private static final int MSG_START_NEW_SESSION = 2; 89 90 private static final long SCREEN_WAKEUP_DELAY = 2500; 91 private static final long USER_INACTIVITY_TIMEOUT_MIN = 10000; 92 private static final long USER_INACTIVITY_TIMEOUT_DEFAULT = 90000; 93 private static final long WARNING_DIALOG_TIMEOUT_DEFAULT = 0; 94 private static final long MILLIS_PER_SECOND = 1000; 95 96 private static final int[] VOLUME_STREAMS_TO_MUTE = { 97 AudioSystem.STREAM_RING, 98 AudioSystem.STREAM_MUSIC 99 }; 100 101 // Tron Vars 102 private static final String DEMO_SESSION_COUNT = "retail_demo_session_count"; 103 private static final String DEMO_SESSION_DURATION = "retail_demo_session_duration"; 104 105 boolean mDeviceInDemoMode = false; 106 int mCurrentUserId = UserHandle.USER_SYSTEM; 107 long mUserInactivityTimeout; 108 long mWarningDialogTimeout; 109 private ActivityManagerService mAms; 110 private ActivityManagerInternal mAmi; 111 private AudioManager mAudioManager; 112 private NotificationManager mNm; 113 private UserManager mUm; 114 private PowerManager mPm; 115 private PowerManager.WakeLock mWakeLock; 116 Handler mHandler; 117 private ServiceThread mHandlerThread; 118 private PendingIntent mResetDemoPendingIntent; 119 private CameraManager mCameraManager; 120 private String[] mCameraIdsWithFlash; 121 private Configuration mSystemUserConfiguration; 122 private PreloadAppsInstaller mPreloadAppsInstaller; 123 124 final Object mActivityLock = new Object(); 125 // Whether the newly created demo user has interacted with the screen yet 126 @GuardedBy("mActivityLock") 127 boolean mUserUntouched; 128 @GuardedBy("mActivityLock") 129 long mFirstUserActivityTime; 130 @GuardedBy("mActivityLock") 131 long mLastUserActivityTime; 132 133 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 134 @Override 135 public void onReceive(Context context, Intent intent) { 136 if (!mDeviceInDemoMode) { 137 return; 138 } 139 switch (intent.getAction()) { 140 case Intent.ACTION_SCREEN_OFF: 141 mHandler.removeMessages(MSG_TURN_SCREEN_ON); 142 mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY); 143 break; 144 case ACTION_RESET_DEMO: 145 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 146 break; 147 } 148 } 149 }; 150 151 final class MainHandler extends Handler { 152 153 MainHandler(Looper looper) { 154 super(looper, null, true); 155 } 156 157 @Override 158 public void handleMessage(Message msg) { 159 switch (msg.what) { 160 case MSG_TURN_SCREEN_ON: 161 if (mWakeLock.isHeld()) { 162 mWakeLock.release(); 163 } 164 mWakeLock.acquire(); 165 break; 166 case MSG_INACTIVITY_TIME_OUT: 167 if (isDemoLauncherDisabled()) { 168 Slog.i(TAG, "User inactivity timeout reached"); 169 showInactivityCountdownDialog(); 170 } 171 break; 172 case MSG_START_NEW_SESSION: 173 if (DEBUG) { 174 Slog.d(TAG, "Switching to a new demo user"); 175 } 176 removeMessages(MSG_START_NEW_SESSION); 177 removeMessages(MSG_INACTIVITY_TIME_OUT); 178 if (mCurrentUserId != UserHandle.USER_SYSTEM) { 179 logSessionDuration(); 180 } 181 final UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME, 182 UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL); 183 if (demoUser != null) { 184 setupDemoUser(demoUser); 185 getActivityManager().switchUser(demoUser.id); 186 } 187 break; 188 } 189 } 190 } 191 192 private class SettingsObserver extends ContentObserver { 193 194 private final static String KEY_USER_INACTIVITY_TIMEOUT = "user_inactivity_timeout_ms"; 195 private final static String KEY_WARNING_DIALOG_TIMEOUT = "warning_dialog_timeout_ms"; 196 197 private final Uri mDeviceDemoModeUri = Settings.Global 198 .getUriFor(Settings.Global.DEVICE_DEMO_MODE); 199 private final Uri mDeviceProvisionedUri = Settings.Global 200 .getUriFor(Settings.Global.DEVICE_PROVISIONED); 201 private final Uri mRetailDemoConstantsUri = Settings.Global 202 .getUriFor(Settings.Global.RETAIL_DEMO_MODE_CONSTANTS); 203 204 private final KeyValueListParser mParser = new KeyValueListParser(','); 205 206 public SettingsObserver(Handler handler) { 207 super(handler); 208 } 209 210 public void register() { 211 ContentResolver cr = getContext().getContentResolver(); 212 cr.registerContentObserver(mDeviceDemoModeUri, false, this, UserHandle.USER_SYSTEM); 213 cr.registerContentObserver(mDeviceProvisionedUri, false, this, UserHandle.USER_SYSTEM); 214 cr.registerContentObserver(mRetailDemoConstantsUri, false, this, 215 UserHandle.USER_SYSTEM); 216 } 217 218 @Override 219 public void onChange(boolean selfChange, Uri uri) { 220 if (mRetailDemoConstantsUri.equals(uri)) { 221 refreshTimeoutConstants(); 222 return; 223 } 224 if (mDeviceDemoModeUri.equals(uri)) { 225 mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext()); 226 if (mDeviceInDemoMode) { 227 putDeviceInDemoMode(); 228 } else { 229 SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "0"); 230 if (mWakeLock.isHeld()) { 231 mWakeLock.release(); 232 } 233 } 234 } 235 // If device is provisioned and left demo mode - run the cleanup in demo folder 236 if (!mDeviceInDemoMode && isDeviceProvisioned()) { 237 // Run on the bg thread to not block the fg thread 238 BackgroundThread.getHandler().post(new Runnable() { 239 @Override 240 public void run() { 241 if (!deletePreloadsFolderContents()) { 242 Slog.w(TAG, "Failed to delete preloads folder contents"); 243 } 244 } 245 }); 246 } 247 } 248 249 private void refreshTimeoutConstants() { 250 try { 251 mParser.setString(Settings.Global.getString(getContext().getContentResolver(), 252 Settings.Global.RETAIL_DEMO_MODE_CONSTANTS)); 253 } catch (IllegalArgumentException exc) { 254 Slog.e(TAG, "Invalid string passed to KeyValueListParser"); 255 // Consuming the exception to fall back to default values. 256 } 257 mWarningDialogTimeout = mParser.getLong(KEY_WARNING_DIALOG_TIMEOUT, 258 WARNING_DIALOG_TIMEOUT_DEFAULT); 259 mUserInactivityTimeout = mParser.getLong(KEY_USER_INACTIVITY_TIMEOUT, 260 USER_INACTIVITY_TIMEOUT_DEFAULT); 261 mUserInactivityTimeout = Math.max(mUserInactivityTimeout, USER_INACTIVITY_TIMEOUT_MIN); 262 } 263 } 264 265 private void showInactivityCountdownDialog() { 266 UserInactivityCountdownDialog dialog = new UserInactivityCountdownDialog(getContext(), 267 mWarningDialogTimeout, MILLIS_PER_SECOND); 268 dialog.setNegativeButtonClickListener(null); 269 dialog.setPositiveButtonClickListener(new DialogInterface.OnClickListener() { 270 @Override 271 public void onClick(DialogInterface dialog, int which) { 272 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 273 } 274 }); 275 dialog.setOnCountDownExpiredListener(new OnCountDownExpiredListener() { 276 @Override 277 public void onCountDownExpired() { 278 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 279 } 280 }); 281 dialog.show(); 282 } 283 284 public RetailDemoModeService(Context context) { 285 super(context); 286 synchronized (mActivityLock) { 287 mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis(); 288 } 289 } 290 291 private Notification createResetNotification() { 292 return new Notification.Builder(getContext()) 293 .setContentTitle(getContext().getString(R.string.reset_retail_demo_mode_title)) 294 .setContentText(getContext().getString(R.string.reset_retail_demo_mode_text)) 295 .setOngoing(true) 296 .setSmallIcon(R.drawable.platlogo) 297 .setShowWhen(false) 298 .setVisibility(Notification.VISIBILITY_PUBLIC) 299 .setContentIntent(getResetDemoPendingIntent()) 300 .setColor(getContext().getColor(R.color.system_notification_accent_color)) 301 .build(); 302 } 303 304 private PendingIntent getResetDemoPendingIntent() { 305 if (mResetDemoPendingIntent == null) { 306 Intent intent = new Intent(ACTION_RESET_DEMO); 307 mResetDemoPendingIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); 308 } 309 return mResetDemoPendingIntent; 310 } 311 312 boolean isDemoLauncherDisabled() { 313 IPackageManager pm = AppGlobals.getPackageManager(); 314 int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 315 String demoLauncherComponent = getContext().getResources() 316 .getString(R.string.config_demoModeLauncherComponent); 317 try { 318 enabledState = pm.getComponentEnabledSetting( 319 ComponentName.unflattenFromString(demoLauncherComponent), 320 mCurrentUserId); 321 } catch (RemoteException exc) { 322 Slog.e(TAG, "Unable to talk to Package Manager", exc); 323 } 324 return enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 325 } 326 327 private void setupDemoUser(UserInfo userInfo) { 328 UserManager um = getUserManager(); 329 UserHandle user = UserHandle.of(userInfo.id); 330 um.setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, user); 331 um.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true, user); 332 um.setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, true, user); 333 um.setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, true, user); 334 um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); 335 um.setUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH, true, user); 336 // Set this to false because the default is true on user creation 337 um.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, user); 338 // Disallow rebooting in safe mode - controlled by user 0 339 getUserManager().setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, true, 340 UserHandle.SYSTEM); 341 Settings.Secure.putIntForUser(getContext().getContentResolver(), 342 Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id); 343 Settings.Global.putInt(getContext().getContentResolver(), 344 Settings.Global.PACKAGE_VERIFIER_ENABLE, 0); 345 grantRuntimePermissionToCamera(user); 346 clearPrimaryCallLog(); 347 } 348 349 private void grantRuntimePermissionToCamera(UserHandle user) { 350 final Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 351 final PackageManager pm = getContext().getPackageManager(); 352 final ResolveInfo handler = pm.resolveActivityAsUser(cameraIntent, 353 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 354 user.getIdentifier()); 355 if (handler == null || handler.activityInfo == null) { 356 return; 357 } 358 try { 359 pm.grantRuntimePermission(handler.activityInfo.packageName, 360 Manifest.permission.ACCESS_FINE_LOCATION, user); 361 } catch (Exception e) { 362 // Ignore 363 } 364 } 365 366 private void clearPrimaryCallLog() { 367 final ContentResolver resolver = getContext().getContentResolver(); 368 369 // Deleting primary user call log so that it doesn't get copied to the new demo user 370 final Uri uri = CallLog.Calls.CONTENT_URI; 371 try { 372 resolver.delete(uri, null, null); 373 } catch (Exception e) { 374 Slog.w(TAG, "Deleting call log failed: " + e); 375 } 376 } 377 378 void logSessionDuration() { 379 final int sessionDuration; 380 synchronized (mActivityLock) { 381 sessionDuration = (int) ((mLastUserActivityTime - mFirstUserActivityTime) / 1000); 382 } 383 MetricsLogger.histogram(getContext(), DEMO_SESSION_DURATION, sessionDuration); 384 } 385 386 private ActivityManagerService getActivityManager() { 387 if (mAms == null) { 388 mAms = (ActivityManagerService) ActivityManagerNative.getDefault(); 389 } 390 return mAms; 391 } 392 393 private UserManager getUserManager() { 394 if (mUm == null) { 395 mUm = getContext().getSystemService(UserManager.class); 396 } 397 return mUm; 398 } 399 400 private AudioManager getAudioManager() { 401 if (mAudioManager == null) { 402 mAudioManager = getContext().getSystemService(AudioManager.class); 403 } 404 return mAudioManager; 405 } 406 407 private boolean isDeviceProvisioned() { 408 return Settings.Global.getInt( 409 getContext().getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; 410 } 411 412 private boolean deletePreloadsFolderContents() { 413 final File dir = Environment.getDataPreloadsDirectory(); 414 Slog.i(TAG, "Deleting contents of " + dir); 415 return FileUtils.deleteContents(dir); 416 } 417 418 private void registerBroadcastReceiver() { 419 final IntentFilter filter = new IntentFilter(); 420 filter.addAction(Intent.ACTION_SCREEN_OFF); 421 filter.addAction(ACTION_RESET_DEMO); 422 getContext().registerReceiver(mBroadcastReceiver, filter); 423 } 424 425 private String[] getCameraIdsWithFlash() { 426 ArrayList<String> cameraIdsList = new ArrayList<String>(); 427 try { 428 for (String cameraId : mCameraManager.getCameraIdList()) { 429 CameraCharacteristics c = mCameraManager.getCameraCharacteristics(cameraId); 430 if (Boolean.TRUE.equals(c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE))) { 431 cameraIdsList.add(cameraId); 432 } 433 } 434 } catch (CameraAccessException e) { 435 Slog.e(TAG, "Unable to access camera while getting camera id list", e); 436 } 437 return cameraIdsList.toArray(new String[cameraIdsList.size()]); 438 } 439 440 private void turnOffAllFlashLights() { 441 for (String cameraId : mCameraIdsWithFlash) { 442 try { 443 mCameraManager.setTorchMode(cameraId, false); 444 } catch (CameraAccessException e) { 445 Slog.e(TAG, "Unable to access camera " + cameraId + " while turning off flash", e); 446 } 447 } 448 } 449 450 private void muteVolumeStreams() { 451 for (int stream : VOLUME_STREAMS_TO_MUTE) { 452 getAudioManager().setStreamVolume(stream, getAudioManager().getStreamMinVolume(stream), 453 0); 454 } 455 } 456 457 private Configuration getSystemUsersConfiguration() { 458 if (mSystemUserConfiguration == null) { 459 Settings.System.getConfiguration(getContext().getContentResolver(), 460 mSystemUserConfiguration = new Configuration()); 461 } 462 return mSystemUserConfiguration; 463 } 464 465 private void putDeviceInDemoMode() { 466 SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "1"); 467 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 468 } 469 470 @Override 471 public void onStart() { 472 if (DEBUG) { 473 Slog.d(TAG, "Service starting up"); 474 } 475 mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, 476 false); 477 mHandlerThread.start(); 478 mHandler = new MainHandler(mHandlerThread.getLooper()); 479 publishLocalService(RetailDemoModeServiceInternal.class, mLocalService); 480 } 481 482 @Override 483 public void onBootPhase(int bootPhase) { 484 switch (bootPhase) { 485 case PHASE_THIRD_PARTY_APPS_CAN_START: 486 mPreloadAppsInstaller = new PreloadAppsInstaller(getContext()); 487 mPm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 488 mAmi = LocalServices.getService(ActivityManagerInternal.class); 489 mWakeLock = mPm 490 .newWakeLock( 491 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 492 TAG); 493 mNm = NotificationManager.from(getContext()); 494 mCameraManager = (CameraManager) getContext() 495 .getSystemService(Context.CAMERA_SERVICE); 496 mCameraIdsWithFlash = getCameraIdsWithFlash(); 497 SettingsObserver settingsObserver = new SettingsObserver(mHandler); 498 settingsObserver.register(); 499 settingsObserver.refreshTimeoutConstants(); 500 registerBroadcastReceiver(); 501 break; 502 case PHASE_BOOT_COMPLETED: 503 if (UserManager.isDeviceInDemoMode(getContext())) { 504 mDeviceInDemoMode = true; 505 putDeviceInDemoMode(); 506 } 507 break; 508 } 509 } 510 511 @Override 512 public void onSwitchUser(int userId) { 513 if (!mDeviceInDemoMode) { 514 return; 515 } 516 if (DEBUG) { 517 Slog.d(TAG, "onSwitchUser: " + userId); 518 } 519 final UserInfo ui = getUserManager().getUserInfo(userId); 520 if (!ui.isDemo()) { 521 Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode"); 522 return; 523 } 524 if (!mWakeLock.isHeld()) { 525 mWakeLock.acquire(); 526 } 527 mCurrentUserId = userId; 528 mAmi.updatePersistentConfigurationForUser(getSystemUsersConfiguration(), userId); 529 turnOffAllFlashLights(); 530 muteVolumeStreams(); 531 // Disable lock screen for demo users. 532 LockPatternUtils lockPatternUtils = new LockPatternUtils(getContext()); 533 lockPatternUtils.setLockScreenDisabled(true, userId); 534 mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); 535 536 synchronized (mActivityLock) { 537 mUserUntouched = true; 538 } 539 MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1); 540 mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); 541 mHandler.post(new Runnable() { 542 @Override 543 public void run() { 544 mPreloadAppsInstaller.installApps(userId); 545 } 546 }); 547 } 548 549 private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() { 550 private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000; 551 552 @Override 553 public void onUserActivity() { 554 if (!mDeviceInDemoMode) { 555 return; 556 } 557 long timeOfActivity = SystemClock.uptimeMillis(); 558 synchronized (mActivityLock) { 559 if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) { 560 return; 561 } 562 mLastUserActivityTime = timeOfActivity; 563 if (mUserUntouched && isDemoLauncherDisabled()) { 564 Slog.d(TAG, "retail_demo first touch"); 565 mUserUntouched = false; 566 mFirstUserActivityTime = timeOfActivity; 567 } 568 } 569 mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); 570 mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, mUserInactivityTimeout); 571 } 572 }; 573 } 574