RetailDemoModeService.java revision 6472501f2e7ba018d8aa43c61e55874d756cecb8
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.retaildemo; 18 19import android.app.ActivityManagerInternal; 20import android.app.ActivityManagerNative; 21import android.app.AppGlobals; 22import android.app.Notification; 23import android.app.NotificationManager; 24import android.app.PendingIntent; 25import android.app.RetailDemoModeServiceInternal; 26import android.content.BroadcastReceiver; 27import android.content.ComponentName; 28import android.content.ContentResolver; 29import android.content.Context; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.IntentFilter; 33import android.content.pm.IPackageManager; 34import android.content.pm.PackageManager; 35import android.content.pm.UserInfo; 36import android.content.res.Configuration; 37import android.database.ContentObserver; 38import android.hardware.camera2.CameraAccessException; 39import android.hardware.camera2.CameraCharacteristics; 40import android.hardware.camera2.CameraManager; 41import android.media.AudioManager; 42import android.media.AudioSystem; 43import android.net.Uri; 44import android.os.Environment; 45import android.os.FileUtils; 46import android.os.Handler; 47import android.os.Looper; 48import android.os.Message; 49import android.os.PowerManager; 50import android.os.RemoteException; 51import android.os.SystemClock; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.provider.Settings; 55import android.util.Slog; 56import com.android.internal.os.BackgroundThread; 57import com.android.internal.R; 58import com.android.internal.widget.LockPatternUtils; 59import com.android.server.LocalServices; 60import com.android.server.ServiceThread; 61import com.android.server.SystemService; 62import com.android.server.am.ActivityManagerService; 63import com.android.server.retaildemo.UserInactivityCountdownDialog.OnCountDownExpiredListener; 64 65import java.io.File; 66import java.util.ArrayList; 67 68public class RetailDemoModeService extends SystemService { 69 private static final boolean DEBUG = false; 70 71 private static final String TAG = RetailDemoModeService.class.getSimpleName(); 72 private static final String DEMO_USER_NAME = "Demo"; 73 private static final String ACTION_RESET_DEMO = "com.android.server.am.ACTION_RESET_DEMO"; 74 75 private static final int MSG_TURN_SCREEN_ON = 0; 76 private static final int MSG_INACTIVITY_TIME_OUT = 1; 77 private static final int MSG_START_NEW_SESSION = 2; 78 79 private static final long SCREEN_WAKEUP_DELAY = 2500; 80 private static final long USER_INACTIVITY_TIMEOUT = 30000; 81 private static final long WARNING_DIALOG_TIMEOUT = 6000; 82 private static final long MILLIS_PER_SECOND = 1000; 83 84 private static final int[] VOLUME_STREAMS_TO_MUTE = { 85 AudioSystem.STREAM_RING, 86 AudioSystem.STREAM_MUSIC 87 }; 88 89 boolean mDeviceInDemoMode = false; 90 int mCurrentUserId; 91 private ActivityManagerService mAms; 92 private ActivityManagerInternal mAmi; 93 private AudioManager mAudioManager; 94 private NotificationManager mNm; 95 private UserManager mUm; 96 private PowerManager mPm; 97 private PowerManager.WakeLock mWakeLock; 98 Handler mHandler; 99 private ServiceThread mHandlerThread; 100 private PendingIntent mResetDemoPendingIntent; 101 private CameraManager mCameraManager; 102 private String[] mCameraIdsWithFlash; 103 private Configuration mPrimaryUserConfiguration; 104 105 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 106 @Override 107 public void onReceive(Context context, Intent intent) { 108 if (!mDeviceInDemoMode) { 109 return; 110 } 111 switch (intent.getAction()) { 112 case Intent.ACTION_SCREEN_OFF: 113 mHandler.removeMessages(MSG_TURN_SCREEN_ON); 114 mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY); 115 break; 116 case ACTION_RESET_DEMO: 117 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 118 break; 119 } 120 } 121 }; 122 123 final class MainHandler extends Handler { 124 125 MainHandler(Looper looper) { 126 super(looper, null, true); 127 } 128 129 @Override 130 public void handleMessage(Message msg) { 131 switch (msg.what) { 132 case MSG_TURN_SCREEN_ON: 133 if (mWakeLock.isHeld()) { 134 mWakeLock.release(); 135 } 136 mWakeLock.acquire(); 137 break; 138 case MSG_INACTIVITY_TIME_OUT: 139 final IPackageManager pm = AppGlobals.getPackageManager(); 140 int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 141 String demoLauncherComponent = getContext().getResources() 142 .getString(R.string.config_demoModeLauncherComponent); 143 try { 144 enabledState = pm.getComponentEnabledSetting( 145 ComponentName.unflattenFromString(demoLauncherComponent), 146 mCurrentUserId); 147 } catch (RemoteException exc) { 148 Slog.e(TAG, "Unable to talk to Package Manager", exc); 149 } 150 if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { 151 Slog.i(TAG, "User inactivity timeout reached"); 152 showInactivityCountdownDialog(); 153 } 154 break; 155 case MSG_START_NEW_SESSION: 156 if (DEBUG) { 157 Slog.d(TAG, "Switching to a new demo user"); 158 } 159 removeMessages(MSG_START_NEW_SESSION); 160 removeMessages(MSG_INACTIVITY_TIME_OUT); 161 final UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME, 162 UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL); 163 if (demoUser != null) { 164 setupDemoUser(demoUser); 165 getActivityManager().switchUser(demoUser.id); 166 } 167 break; 168 } 169 } 170 } 171 172 private void showInactivityCountdownDialog() { 173 UserInactivityCountdownDialog dialog = new UserInactivityCountdownDialog(getContext(), 174 WARNING_DIALOG_TIMEOUT, MILLIS_PER_SECOND); 175 dialog.setPositiveButtonClickListener(null); 176 dialog.setNegativeButtonClickListener(new DialogInterface.OnClickListener() { 177 @Override 178 public void onClick(DialogInterface dialog, int which) { 179 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 180 } 181 }); 182 dialog.setOnCountDownExpiredListener(new OnCountDownExpiredListener() { 183 @Override 184 public void onCountDownExpired() { 185 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 186 } 187 }); 188 dialog.show(); 189 } 190 191 public RetailDemoModeService(Context context) { 192 super(context); 193 } 194 195 private Notification createResetNotification() { 196 return new Notification.Builder(getContext()) 197 .setContentTitle(getContext().getString(R.string.reset_retail_demo_mode_title)) 198 .setContentText(getContext().getString(R.string.reset_retail_demo_mode_text)) 199 .setOngoing(true) 200 .setSmallIcon(R.drawable.platlogo) 201 .setShowWhen(false) 202 .setVisibility(Notification.VISIBILITY_PUBLIC) 203 .setContentIntent(getResetDemoPendingIntent()) 204 .setColor(getContext().getColor(R.color.system_notification_accent_color)) 205 .build(); 206 } 207 208 private PendingIntent getResetDemoPendingIntent() { 209 if (mResetDemoPendingIntent == null) { 210 Intent intent = new Intent(ACTION_RESET_DEMO); 211 mResetDemoPendingIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); 212 } 213 return mResetDemoPendingIntent; 214 } 215 216 private void setupDemoUser(UserInfo userInfo) { 217 UserManager um = getUserManager(); 218 UserHandle user = UserHandle.of(userInfo.id); 219 LockPatternUtils lockPatternUtils = new LockPatternUtils(getContext()); 220 lockPatternUtils.setLockScreenDisabled(true, userInfo.id); 221 um.setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, user); 222 um.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true, user); 223 um.setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, true, user); 224 um.setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, true, user); 225 Settings.Secure.putIntForUser(getContext().getContentResolver(), 226 Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id); 227 } 228 229 private ActivityManagerService getActivityManager() { 230 if (mAms == null) { 231 mAms = (ActivityManagerService) ActivityManagerNative.getDefault(); 232 } 233 return mAms; 234 } 235 236 private UserManager getUserManager() { 237 if (mUm == null) { 238 mUm = getContext().getSystemService(UserManager.class); 239 } 240 return mUm; 241 } 242 243 private AudioManager getAudioManager() { 244 if (mAudioManager == null) { 245 mAudioManager = getContext().getSystemService(AudioManager.class); 246 } 247 return mAudioManager; 248 } 249 250 private void registerSettingsChangeObserver() { 251 final Uri deviceDemoModeUri = Settings.Global.getUriFor(Settings.Global.DEVICE_DEMO_MODE); 252 final Uri deviceProvisionedUri = Settings.Global.getUriFor( 253 Settings.Global.DEVICE_PROVISIONED); 254 final ContentResolver cr = getContext().getContentResolver(); 255 final ContentObserver deviceDemoModeSettingObserver = new ContentObserver(mHandler) { 256 @Override 257 public void onChange(boolean selfChange, Uri uri, int userId) { 258 if (deviceDemoModeUri.equals(uri)) { 259 mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext()); 260 if (mDeviceInDemoMode) { 261 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 262 } else if (mWakeLock.isHeld()) { 263 mWakeLock.release(); 264 } 265 } 266 // If device is provisioned and left demo mode - run the cleanup in demo folder 267 if (!mDeviceInDemoMode && isDeviceProvisioned()) { 268 // Run on the bg thread to not block the fg thread 269 BackgroundThread.getHandler().post(new Runnable() { 270 @Override 271 public void run() { 272 if (!deleteDemoFolderContents()) { 273 Slog.w(TAG, "Failed to delete demo folder contents"); 274 } 275 } 276 }); 277 } 278 } 279 }; 280 cr.registerContentObserver(deviceDemoModeUri, false, deviceDemoModeSettingObserver, 281 UserHandle.USER_SYSTEM); 282 cr.registerContentObserver(deviceProvisionedUri, false, deviceDemoModeSettingObserver, 283 UserHandle.USER_SYSTEM); 284 } 285 286 private boolean isDeviceProvisioned() { 287 return Settings.Global.getInt( 288 getContext().getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; 289 } 290 291 private boolean deleteDemoFolderContents() { 292 final File dir = Environment.getDataPreloadsDemoDirectory(); 293 Slog.i(TAG, "Deleting contents of " + dir); 294 return FileUtils.deleteContents(dir); 295 } 296 297 private void registerBroadcastReceiver() { 298 final IntentFilter filter = new IntentFilter(); 299 filter.addAction(Intent.ACTION_SCREEN_OFF); 300 filter.addAction(ACTION_RESET_DEMO); 301 getContext().registerReceiver(mBroadcastReceiver, filter); 302 } 303 304 private String[] getCameraIdsWithFlash() { 305 ArrayList<String> cameraIdsList = new ArrayList<String>(); 306 try { 307 for (String cameraId : mCameraManager.getCameraIdList()) { 308 CameraCharacteristics c = mCameraManager.getCameraCharacteristics(cameraId); 309 if (Boolean.TRUE.equals(c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE))) { 310 cameraIdsList.add(cameraId); 311 } 312 } 313 } catch (CameraAccessException e) { 314 Slog.e(TAG, "Unable to access camera while getting camera id list", e); 315 } 316 return cameraIdsList.toArray(new String[cameraIdsList.size()]); 317 } 318 319 private void turnOffAllFlashLights() { 320 for (String cameraId : mCameraIdsWithFlash) { 321 try { 322 mCameraManager.setTorchMode(cameraId, false); 323 } catch (CameraAccessException e) { 324 Slog.e(TAG, "Unable to access camera " + cameraId + " while turning off flash", e); 325 } 326 } 327 } 328 329 private void muteVolumeStreams() { 330 for (int stream : VOLUME_STREAMS_TO_MUTE) { 331 getAudioManager().setStreamVolume(stream, getAudioManager().getStreamMinVolume(stream), 332 0); 333 } 334 } 335 336 private Configuration getPrimaryUsersConfiguration() { 337 if (mPrimaryUserConfiguration == null) { 338 Settings.System.getConfiguration(getContext().getContentResolver(), 339 mPrimaryUserConfiguration = new Configuration()); 340 } 341 return mPrimaryUserConfiguration; 342 } 343 344 @Override 345 public void onStart() { 346 if (DEBUG) { 347 Slog.d(TAG, "Service starting up"); 348 } 349 mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, 350 false); 351 mHandlerThread.start(); 352 mHandler = new MainHandler(mHandlerThread.getLooper()); 353 publishLocalService(RetailDemoModeServiceInternal.class, mLocalService); 354 } 355 356 @Override 357 public void onBootPhase(int bootPhase) { 358 if (bootPhase != PHASE_THIRD_PARTY_APPS_CAN_START) { 359 return; 360 } 361 mPm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 362 mAmi = LocalServices.getService(ActivityManagerInternal.class); 363 mWakeLock = mPm 364 .newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG); 365 mNm = NotificationManager.from(getContext()); 366 mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); 367 mCameraIdsWithFlash = getCameraIdsWithFlash(); 368 369 if (UserManager.isDeviceInDemoMode(getContext())) { 370 mDeviceInDemoMode = true; 371 mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); 372 } 373 registerSettingsChangeObserver(); 374 registerBroadcastReceiver(); 375 } 376 377 @Override 378 public void onSwitchUser(int userId) { 379 if (!mDeviceInDemoMode) { 380 return; 381 } 382 if (DEBUG) { 383 Slog.d(TAG, "onSwitchUser: " + userId); 384 } 385 final UserInfo ui = getUserManager().getUserInfo(userId); 386 if (!ui.isDemo()) { 387 Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode"); 388 return; 389 } 390 if (!mWakeLock.isHeld()) { 391 mWakeLock.acquire(); 392 } 393 mCurrentUserId = userId; 394 mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); 395 turnOffAllFlashLights(); 396 muteVolumeStreams(); 397 mAmi.updatePersistentConfigurationForUser(getPrimaryUsersConfiguration(), userId); 398 } 399 400 private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() { 401 private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000; 402 private long mLastUserActivityTime = 0; 403 404 @Override 405 public void onUserActivity() { 406 if (!mDeviceInDemoMode) { 407 return; 408 } 409 long timeOfActivity = SystemClock.uptimeMillis(); 410 if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) { 411 return; 412 } 413 mLastUserActivityTime = timeOfActivity; 414 mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); 415 mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, USER_INACTIVITY_TIMEOUT); 416 } 417 }; 418} 419