diff --git a/services/core/java/com/android/server/am/RetailDemoModeService.java b/services/core/java/com/android/server/am/RetailDemoModeService.java index a0d1c2466da77..6a5ec9697a419 100644 --- a/services/core/java/com/android/server/am/RetailDemoModeService.java +++ b/services/core/java/com/android/server/am/RetailDemoModeService.java @@ -17,22 +17,29 @@ package com.android.server.am; import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.PowerManager; -import android.os.ServiceManager; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -43,8 +50,6 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.pm.UserManagerService; - import java.io.File; public class RetailDemoModeService extends SystemService { @@ -54,42 +59,89 @@ public class RetailDemoModeService extends SystemService { private static final String DEMO_USER_NAME = "Demo"; private static final String ACTION_RESET_DEMO = "com.android.server.am.ACTION_RESET_DEMO"; - private static final long SCREEN_WAKEUP_DELAY = 5000; + private static final int MSG_TURN_SCREEN_ON = 0; + private static final int MSG_INACTIVITY_TIME_OUT = 1; + private static final int MSG_START_NEW_SESSION = 2; + private static final long SCREEN_WAKEUP_DELAY = 2500; + private static final long USER_INACTIVITY_TIMEOUT = 30000; + + boolean mDeviceInDemoMode = false; private ActivityManagerService mAms; private NotificationManager mNm; private UserManager mUm; private PowerManager mPm; private PowerManager.WakeLock mWakeLock; - private Handler mHandler; + Handler mHandler; private ServiceThread mHandlerThread; private PendingIntent mResetDemoPendingIntent; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (!UserManager.isDeviceInDemoMode(getContext())) { + if (!mDeviceInDemoMode) { return; } switch (intent.getAction()) { case Intent.ACTION_SCREEN_OFF: - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } - mWakeLock.acquire(); - } - }, SCREEN_WAKEUP_DELAY); + mHandler.removeMessages(MSG_TURN_SCREEN_ON); + mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY); break; case ACTION_RESET_DEMO: - createAndSwitchToDemoUser(); + mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); break; } } }; + final class MainHandler extends Handler { + + MainHandler(Looper looper) { + super(looper, null, true); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TURN_SCREEN_ON: + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + mWakeLock.acquire(); + break; + case MSG_INACTIVITY_TIME_OUT: + IPackageManager pm = AppGlobals.getPackageManager(); + int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + String demoLauncherComponent = getContext().getResources() + .getString(R.string.config_demoModeLauncherComponent); + try { + enabledState = pm.getComponentEnabledSetting( + ComponentName.unflattenFromString(demoLauncherComponent), + getActivityManager().getCurrentUser().id); + } catch (RemoteException exc) { + // XXX: shouldn't happen + } + if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + Slog.i(TAG, "Restarting session due to user inactivity timeout"); + sendEmptyMessage(MSG_START_NEW_SESSION); + } + break; + case MSG_START_NEW_SESSION: + if (DEBUG) { + Slog.d(TAG, "Switching to a new demo user"); + } + removeMessages(MSG_START_NEW_SESSION); + UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME, + UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL); + if (demoUser != null) { + setupDemoUser(demoUser); + getActivityManager().switchUser(demoUser.id); + } + break; + } + } + } + public RetailDemoModeService(Context context) { super(context); } @@ -103,6 +155,7 @@ public class RetailDemoModeService extends SystemService { .setShowWhen(false) .setVisibility(Notification.VISIBILITY_PUBLIC) .setContentIntent(getResetDemoPendingIntent()) + .setColor(getContext().getColor(R.color.system_notification_accent_color)) .build(); } @@ -114,23 +167,6 @@ public class RetailDemoModeService extends SystemService { return mResetDemoPendingIntent; } - private void createAndSwitchToDemoUser() { - if (DEBUG) { - Slog.d(TAG, "Switching to a new demo user"); - } - mHandler.post(new Runnable() { - @Override - public void run() { - UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME, - UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL); - if (demoUser != null) { - setupDemoUser(demoUser); - getActivityManager().switchUser(demoUser.id); - } - } - }); - } - void setupDemoUser(UserInfo userInfo) { UserManager um = getUserManager(); UserHandle user = UserHandle.of(userInfo.id); @@ -166,14 +202,16 @@ public class RetailDemoModeService extends SystemService { final ContentObserver deviceDemoModeSettingObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange, Uri uri, int userId) { - boolean deviceInDemoMode = UserManager.isDeviceInDemoMode(getContext()); if (deviceDemoModeUri.equals(uri)) { - if (deviceInDemoMode) { - createAndSwitchToDemoUser(); + mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext()); + if (mDeviceInDemoMode) { + mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); + } else if (mWakeLock.isHeld()) { + mWakeLock.release(); } } // If device is provisioned and left demo mode - run the cleanup in demo folder - if (!deviceInDemoMode && isDeviceProvisioned()) { + if (!mDeviceInDemoMode && isDeviceProvisioned()) { // Run on the bg thread to not block the fg thread BackgroundThread.getHandler().post(new Runnable() { @Override @@ -218,7 +256,8 @@ public class RetailDemoModeService extends SystemService { mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, false); mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper(), null, true); + mHandler = new MainHandler(mHandlerThread.getLooper()); + publishLocalService(RetailDemoModeServiceInternal.class, mLocalService); } @Override @@ -232,7 +271,8 @@ public class RetailDemoModeService extends SystemService { mNm = NotificationManager.from(getContext()); if (UserManager.isDeviceInDemoMode(getContext())) { - createAndSwitchToDemoUser(); + mDeviceInDemoMode = true; + mHandler.sendEmptyMessage(MSG_START_NEW_SESSION); } registerSettingsChangeObserver(); registerBroadcastReceiver(); @@ -240,16 +280,15 @@ public class RetailDemoModeService extends SystemService { @Override public void onSwitchUser(int userId) { + if (!mDeviceInDemoMode) { + return; + } if (DEBUG) { Slog.d(TAG, "onSwitchUser: " + userId); } UserInfo ui = getUserManager().getUserInfo(userId); if (!ui.isDemo()) { - if (UserManager.isDeviceInDemoMode(getContext())) { - Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode"); - } else if (mWakeLock.isHeld()) { - mWakeLock.release(); - } + Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode"); return; } if (!mWakeLock.isHeld()) { @@ -257,4 +296,23 @@ public class RetailDemoModeService extends SystemService { } mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); } + + public RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() { + private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000; + private long mLastUserActivityTime = 0; + + @Override + public void onUserActivity() { + if (!mDeviceInDemoMode) { + return; + } + long timeOfActivity = SystemClock.uptimeMillis(); + if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) { + return; + } + mLastUserActivityTime = timeOfActivity; + mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); + mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, USER_INACTIVITY_TIMEOUT); + } + }; } diff --git a/services/core/java/com/android/server/am/RetailDemoModeServiceInternal.java b/services/core/java/com/android/server/am/RetailDemoModeServiceInternal.java new file mode 100644 index 0000000000000..32de03a31cb9b --- /dev/null +++ b/services/core/java/com/android/server/am/RetailDemoModeServiceInternal.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +public interface RetailDemoModeServiceInternal { + public void onUserActivity(); +} \ No newline at end of file diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 7108f4afb192e..3ed6ec90e8459 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -23,6 +23,7 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.server.EventLogTags; import com.android.server.LocalServices; +import com.android.server.am.RetailDemoModeServiceInternal; import android.app.ActivityManagerNative; import android.content.BroadcastReceiver; @@ -91,6 +92,7 @@ final class Notifier { private final ActivityManagerInternal mActivityManagerInternal; private final InputManagerInternal mInputManagerInternal; private final InputMethodManagerInternal mInputMethodManagerInternal; + private final RetailDemoModeServiceInternal mRetailDemoModeServiceInternal; private final NotifierHandler mHandler; private final Intent mScreenOnIntent; @@ -136,6 +138,7 @@ final class Notifier { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); + mRetailDemoModeServiceInternal = LocalServices.getService(RetailDemoModeServiceInternal.class); mHandler = new NotifierHandler(looper); mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); @@ -534,7 +537,9 @@ final class Notifier { } mUserActivityPending = false; } - + if (mRetailDemoModeServiceInternal != null) { + mRetailDemoModeServiceInternal.onUserActivity(); + } mPolicy.userActivity(); }