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