Merge "Adding an idle timeout to restart demo session" into nyc-mr1-dev

This commit is contained in:
TreeHugger Robot
2016-06-03 00:18:16 +00:00
committed by Android (Google) Code Review
3 changed files with 129 additions and 45 deletions

View File

@@ -17,22 +17,29 @@
package com.android.server.am; package com.android.server.am;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.os.FileUtils; import android.os.FileUtils;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.ServiceManager; import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
@@ -43,8 +50,6 @@ import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.server.ServiceThread; import com.android.server.ServiceThread;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
import java.io.File; import java.io.File;
public class RetailDemoModeService extends SystemService { 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 DEMO_USER_NAME = "Demo";
private static final String ACTION_RESET_DEMO = "com.android.server.am.ACTION_RESET_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 ActivityManagerService mAms;
private NotificationManager mNm; private NotificationManager mNm;
private UserManager mUm; private UserManager mUm;
private PowerManager mPm; private PowerManager mPm;
private PowerManager.WakeLock mWakeLock; private PowerManager.WakeLock mWakeLock;
private Handler mHandler; Handler mHandler;
private ServiceThread mHandlerThread; private ServiceThread mHandlerThread;
private PendingIntent mResetDemoPendingIntent; private PendingIntent mResetDemoPendingIntent;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!UserManager.isDeviceInDemoMode(getContext())) { if (!mDeviceInDemoMode) {
return; return;
} }
switch (intent.getAction()) { switch (intent.getAction()) {
case Intent.ACTION_SCREEN_OFF: case Intent.ACTION_SCREEN_OFF:
mHandler.postDelayed(new Runnable() { mHandler.removeMessages(MSG_TURN_SCREEN_ON);
@Override mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY);
public void run() {
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
mWakeLock.acquire();
}
}, SCREEN_WAKEUP_DELAY);
break; break;
case ACTION_RESET_DEMO: case ACTION_RESET_DEMO:
createAndSwitchToDemoUser(); mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
break; 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) { public RetailDemoModeService(Context context) {
super(context); super(context);
} }
@@ -103,6 +155,7 @@ public class RetailDemoModeService extends SystemService {
.setShowWhen(false) .setShowWhen(false)
.setVisibility(Notification.VISIBILITY_PUBLIC) .setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(getResetDemoPendingIntent()) .setContentIntent(getResetDemoPendingIntent())
.setColor(getContext().getColor(R.color.system_notification_accent_color))
.build(); .build();
} }
@@ -114,23 +167,6 @@ public class RetailDemoModeService extends SystemService {
return mResetDemoPendingIntent; 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) { void setupDemoUser(UserInfo userInfo) {
UserManager um = getUserManager(); UserManager um = getUserManager();
UserHandle user = UserHandle.of(userInfo.id); UserHandle user = UserHandle.of(userInfo.id);
@@ -166,14 +202,16 @@ public class RetailDemoModeService extends SystemService {
final ContentObserver deviceDemoModeSettingObserver = new ContentObserver(mHandler) { final ContentObserver deviceDemoModeSettingObserver = new ContentObserver(mHandler) {
@Override @Override
public void onChange(boolean selfChange, Uri uri, int userId) { public void onChange(boolean selfChange, Uri uri, int userId) {
boolean deviceInDemoMode = UserManager.isDeviceInDemoMode(getContext());
if (deviceDemoModeUri.equals(uri)) { if (deviceDemoModeUri.equals(uri)) {
if (deviceInDemoMode) { mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext());
createAndSwitchToDemoUser(); 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 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 // Run on the bg thread to not block the fg thread
BackgroundThread.getHandler().post(new Runnable() { BackgroundThread.getHandler().post(new Runnable() {
@Override @Override
@@ -218,7 +256,8 @@ public class RetailDemoModeService extends SystemService {
mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND,
false); false);
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), null, true); mHandler = new MainHandler(mHandlerThread.getLooper());
publishLocalService(RetailDemoModeServiceInternal.class, mLocalService);
} }
@Override @Override
@@ -232,7 +271,8 @@ public class RetailDemoModeService extends SystemService {
mNm = NotificationManager.from(getContext()); mNm = NotificationManager.from(getContext());
if (UserManager.isDeviceInDemoMode(getContext())) { if (UserManager.isDeviceInDemoMode(getContext())) {
createAndSwitchToDemoUser(); mDeviceInDemoMode = true;
mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
} }
registerSettingsChangeObserver(); registerSettingsChangeObserver();
registerBroadcastReceiver(); registerBroadcastReceiver();
@@ -240,16 +280,15 @@ public class RetailDemoModeService extends SystemService {
@Override @Override
public void onSwitchUser(int userId) { public void onSwitchUser(int userId) {
if (!mDeviceInDemoMode) {
return;
}
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "onSwitchUser: " + userId); Slog.d(TAG, "onSwitchUser: " + userId);
} }
UserInfo ui = getUserManager().getUserInfo(userId); UserInfo ui = getUserManager().getUserInfo(userId);
if (!ui.isDemo()) { if (!ui.isDemo()) {
if (UserManager.isDeviceInDemoMode(getContext())) {
Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode"); Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode");
} else if (mWakeLock.isHeld()) {
mWakeLock.release();
}
return; return;
} }
if (!mWakeLock.isHeld()) { if (!mWakeLock.isHeld()) {
@@ -257,4 +296,23 @@ public class RetailDemoModeService extends SystemService {
} }
mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId)); 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);
}
};
} }

View File

@@ -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();
}

View File

@@ -23,6 +23,7 @@ import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats; import com.android.internal.app.IBatteryStats;
import com.android.server.EventLogTags; import com.android.server.EventLogTags;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.am.RetailDemoModeServiceInternal;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@@ -91,6 +92,7 @@ final class Notifier {
private final ActivityManagerInternal mActivityManagerInternal; private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal; private final InputManagerInternal mInputManagerInternal;
private final InputMethodManagerInternal mInputMethodManagerInternal; private final InputMethodManagerInternal mInputMethodManagerInternal;
private final RetailDemoModeServiceInternal mRetailDemoModeServiceInternal;
private final NotifierHandler mHandler; private final NotifierHandler mHandler;
private final Intent mScreenOnIntent; private final Intent mScreenOnIntent;
@@ -136,6 +138,7 @@ final class Notifier {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mRetailDemoModeServiceInternal = LocalServices.getService(RetailDemoModeServiceInternal.class);
mHandler = new NotifierHandler(looper); mHandler = new NotifierHandler(looper);
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
@@ -534,7 +537,9 @@ final class Notifier {
} }
mUserActivityPending = false; mUserActivityPending = false;
} }
if (mRetailDemoModeServiceInternal != null) {
mRetailDemoModeServiceInternal.onUserActivity();
}
mPolicy.userActivity(); mPolicy.userActivity();
} }