summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server')
-rw-r--r--services/java/com/android/server/BatteryService.java165
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java11
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java256
-rw-r--r--services/java/com/android/server/ProfileManagerService.java550
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rwxr-xr-xservices/java/com/android/server/VibratorService.java29
-rw-r--r--services/java/com/android/server/power/ShutdownThread.java81
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java6
8 files changed, 1053 insertions, 54 deletions
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index dbffa97..537d69f 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -24,6 +24,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.Color;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.FileUtils;
@@ -44,7 +47,7 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-
+import java.util.Calendar;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -140,10 +143,21 @@ public final class BatteryService extends Binder {
private boolean mUpdatesStopped;
private Led mLed;
+ private boolean mLightEnabled;
+ private boolean mLedPulseEnabled;
+ private int mBatteryLowARGB;
+ private int mBatteryMediumARGB;
+ private int mBatteryFullARGB;
+ private boolean mMultiColorLed;
private boolean mSentLowBatteryBroadcast = false;
private native void native_update();
+ // Quiet hours support
+ private boolean mQuietHoursEnabled = false;
+ private int mQuietHoursStart = 0;
+ private int mQuietHoursEnd = 0;
+ private boolean mQuietHoursDim = true;
public BatteryService(Context context, LightsService lights) {
mContext = context;
@@ -168,6 +182,9 @@ public final class BatteryService extends Binder {
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
+ SettingsObserver observer = new SettingsObserver(new Handler());
+ observer.observe();
+
// set initial status
synchronized (mLock) {
updateLocked();
@@ -672,6 +689,10 @@ public final class BatteryService extends Binder {
}
};
+ private synchronized void updateLedPulse() {
+ mLed.updateLightsLocked();
+ }
+
private final class Led {
private final LightsService.Light mBatteryLight;
@@ -684,12 +705,17 @@ public final class BatteryService extends Binder {
public Led(Context context, LightsService lights) {
mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
- mBatteryLowARGB = context.getResources().getInteger(
+ mBatteryLowARGB = mContext.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLowARGB);
- mBatteryMediumARGB = context.getResources().getInteger(
+ mBatteryMediumARGB = mContext.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
- mBatteryFullARGB = context.getResources().getInteger(
+ mBatteryFullARGB = mContext.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+
+ // Does the Device support changing battery LED colors?
+ mMultiColorLed = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_multiColorBatteryLed);
+
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
mBatteryLedOff = context.getResources().getInteger(
@@ -702,28 +728,143 @@ public final class BatteryService extends Binder {
public void updateLightsLocked() {
final int level = mBatteryLevel;
final int status = mBatteryStatus;
- if (level < mLowBatteryWarningLevel) {
+
+ if (!mLightEnabled) {
+ // No lights if explicitly disabled
+ mBatteryLight.turnOff();
+ } else if (inQuietHours() && mQuietHoursDim) {
+ if (mLedPulseEnabled && level < mLowBatteryWarningLevel &&
+ status != BatteryManager.BATTERY_STATUS_CHARGING) {
+ // The battery is low, the device is not charging and the low battery pulse
+ // is enabled - ignore Quiet Hours
+ mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOff);
+ } else {
+ // No lights if in Quiet Hours and battery not low
+ mBatteryLight.turnOff();
+ }
+ } else if (level < mLowBatteryWarningLevel) {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- // Solid red when battery is charging
+ // Battery is charging and low
mBatteryLight.setColor(mBatteryLowARGB);
- } else {
- // Flash red when battery is low and not charging
+ } else if (mLedPulseEnabled) {
+ // Battery is low and not charging
mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
+ } else {
+ // "Pulse low battery light" is disabled, no lights.
+ mBatteryLight.turnOff();
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
- || status == BatteryManager.BATTERY_STATUS_FULL) {
+ || status == BatteryManager.BATTERY_STATUS_FULL) {
if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
- // Solid green when full or charging and nearly full
+ // Battery is full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
- // Solid orange when charging and halfway full
+ // Battery is charging and halfway full
mBatteryLight.setColor(mBatteryMediumARGB);
}
} else {
- // No lights if not charging and not low
+ //No lights if not charging and not low
mBatteryLight.turnOff();
}
}
}
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Battery light enabled
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_ENABLED), false, this);
+
+ // Low battery pulse
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_PULSE), false, this);
+
+ // Light colors
+ if (mMultiColorLed) {
+ // Register observer if we have a multi color led
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_LOW_COLOR), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_MEDIUM_COLOR), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_FULL_COLOR), false, this);
+ }
+
+ // Quiet Hours
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_ENABLED), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_START), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_END), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_DIM), false, this);
+
+ update();
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Resources res = mContext.getResources();
+
+ // Battery light enabled
+ mLightEnabled = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_ENABLED, 1) != 0;
+
+ // Low battery pulse
+ mLedPulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_PULSE, 1) != 0;
+
+ // Light colors
+ mBatteryLowARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_LOW_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB));
+ mBatteryMediumARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_MEDIUM_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB));
+ mBatteryFullARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_FULL_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB));
+
+ // Quiet Hours
+ mQuietHoursEnabled = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_ENABLED, 0) != 0;
+ mQuietHoursStart = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_START, 0);
+ mQuietHoursEnd = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_END, 0);
+ mQuietHoursDim = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_DIM, 0) != 0;
+
+ updateLedPulse();
+ }
+ }
+
+ private boolean inQuietHours() {
+ if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) {
+ // Get the date in "quiet hours" format.
+ Calendar calendar = Calendar.getInstance();
+ int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE);
+ if (mQuietHoursEnd < mQuietHoursStart) {
+ // Starts at night, ends in the morning.
+ return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd);
+ } else {
+ return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd);
+ }
+ }
+ return false;
+ }
+
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c9ff595..b11432d 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -397,6 +397,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.ENABLED_INPUT_METHODS), false, this);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.STATUS_BAR_IME_SWITCHER),
+ false, new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ updateFromSettingsLocked();
+ }
+ });
}
@Override public void onChange(boolean selfChange) {
@@ -844,8 +851,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
updateImeWindowStatusLocked();
- mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
- com.android.internal.R.bool.show_ongoing_ime_switcher);
if (mShowOngoingImeSwitcherForPhones) {
mWindowManagerService.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
@@ -1597,6 +1602,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
+ mShowOngoingImeSwitcherForPhones = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.STATUS_BAR_IME_SWITCHER, 1) == 1;
}
/* package */ void setInputMethodLocked(String id, int subtypeId) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index f3a38f0..af49135 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -27,7 +27,12 @@ import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.Notification;
+import android.app.NotificationGroup;
+import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.Profile;
+import android.app.ProfileGroup;
+import android.app.ProfileManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -44,6 +49,7 @@ import android.media.IAudioService;
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -81,7 +87,10 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Calendar;
+import java.util.Map;
import libcore.io.IoUtils;
@@ -134,10 +143,13 @@ public class NotificationManagerService extends INotificationManager.Stub
private IAudioService mAudioService;
private Vibrator mVibrator;
- // for enabling and disabling notification pulse behavior
+ // for enabling and disabling notification pulse behaviour
private boolean mScreenOn = true;
+ private boolean mWasScreenOn = false;
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
+ private HashMap<String, NotificationLedValues> mNotificationPulseCustomLedValues;
+ private Map<String, String> mPackageNameMappings;
private final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
@@ -147,6 +159,18 @@ public class NotificationManagerService extends INotificationManager.Stub
private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
private NotificationRecord mLedNotification;
+ private boolean mQuietHoursEnabled = false;
+ // Minutes from midnight when quiet hours begin.
+ private int mQuietHoursStart = 0;
+ // Minutes from midnight when quiet hours end.
+ private int mQuietHoursEnd = 0;
+ // Don't play sounds.
+ private boolean mQuietHoursMute = true;
+ // Don't vibrate.
+ private boolean mQuietHoursStill = true;
+ // Dim LED if hardware supports it.
+ private boolean mQuietHoursDim = true;
+
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
@@ -402,6 +426,12 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ class NotificationLedValues {
+ public int color;
+ public int onMS;
+ public int offMS;
+ }
+
private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks
= new StatusBarManagerService.NotificationCallbacks() {
@@ -553,6 +583,8 @@ public class NotificationManagerService extends INotificationManager.Stub
mScreenOn = true;
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
+ mWasScreenOn = true;
+ updateLightsLocked();
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK));
@@ -569,8 +601,8 @@ public class NotificationManagerService extends INotificationManager.Stub
}
};
- class SettingsObserver extends ContentObserver {
- SettingsObserver(Handler handler) {
+ class LEDSettingsObserver extends ContentObserver {
+ LEDSettingsObserver(Handler handler) {
super(handler);
}
@@ -578,24 +610,96 @@ public class NotificationManagerService extends INotificationManager.Stub
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES), false, this);
update();
}
@Override public void onChange(boolean selfChange) {
update();
+ updateNotificationPulse();
}
public void update() {
ContentResolver resolver = mContext.getContentResolver();
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
+ // LED enabled
+ mNotificationPulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
+
+ // LED default color
+ mDefaultNotificationColor = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, mDefaultNotificationColor);
+
+ // LED default on MS
+ mDefaultNotificationLedOn = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, mDefaultNotificationLedOn);
+
+ // LED default off MS
+ mDefaultNotificationLedOff = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, mDefaultNotificationLedOff);
+
+ // LED custom notification colors
+ mNotificationPulseCustomLedValues.clear();
+ if (Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0) != 0) {
+ parseNotificationPulseCustomValuesString(Settings.System.getString(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES));
}
}
}
+ class QuietHoursSettingsObserver extends ContentObserver {
+ QuietHoursSettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_ENABLED), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_START), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_END), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_MUTE), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_STILL), false, this);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.QUIET_HOURS_DIM), false, this);
+ update();
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ update();
+ updateNotificationPulse();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mQuietHoursEnabled = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_ENABLED, 0) != 0;
+ mQuietHoursStart = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_START, 0);
+ mQuietHoursEnd = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_END, 0);
+ mQuietHoursMute = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_MUTE, 0) != 0;
+ mQuietHoursStill = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_STILL, 0) != 0;
+ mQuietHoursDim = Settings.System.getInt(resolver,
+ Settings.System.QUIET_HOURS_DIM, 0) != 0;
+ }
+ }
+
NotificationManagerService(Context context, StatusBarManagerService statusBar,
LightsService lights)
{
@@ -622,6 +726,15 @@ public class NotificationManagerService extends INotificationManager.Stub
mDefaultNotificationLedOff = resources.getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
+ mNotificationPulseCustomLedValues = new HashMap<String, NotificationLedValues>();
+
+ mPackageNameMappings = new HashMap<String, String>();
+ for(String mapping : resources.getStringArray(
+ com.android.internal.R.array.notification_light_package_mapping)) {
+ String[] map = mapping.split("\\|");
+ mPackageNameMappings.put(map[0], map[1]);
+ }
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -649,8 +762,10 @@ public class NotificationManagerService extends INotificationManager.Stub
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, sdFilter);
- SettingsObserver observer = new SettingsObserver(mHandler);
- observer.observe();
+ LEDSettingsObserver ledObserver = new LEDSettingsObserver(mHandler);
+ ledObserver.observe();
+ QuietHoursSettingsObserver qhObserver = new QuietHoursSettingsObserver(mHandler);
+ qhObserver.observe();
}
void systemReady() {
@@ -965,6 +1080,8 @@ public class NotificationManagerService extends INotificationManager.Stub
}
synchronized (mNotificationList) {
+ final boolean inQuietHours = inQuietHours();
+
NotificationRecord r = new NotificationRecord(pkg, tag, id,
callingUid, callingPid, userId,
score,
@@ -1040,6 +1157,16 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ try {
+ final ProfileManager profileManager =
+ (ProfileManager) mContext.getSystemService(Context.PROFILE_SERVICE);
+
+ ProfileGroup group = profileManager.getActiveProfileGroup(pkg);
+ notification = group.processNotification(notification);
+ } catch(Throwable th) {
+ Log.e(TAG, "An error occurred profiling the notification.", th);
+ }
+
// If we're not supposed to beep, vibrate, etc. then don't.
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
@@ -1053,7 +1180,8 @@ public class NotificationManagerService extends INotificationManager.Stub
// sound
final boolean useDefaultSound =
(notification.defaults & Notification.DEFAULT_SOUND) != 0;
- if (useDefaultSound || notification.sound != null) {
+ if (!(inQuietHours && mQuietHoursMute)
+ && (useDefaultSound || notification.sound != null)) {
Uri uri;
if (useDefaultSound) {
uri = Settings.System.DEFAULT_NOTIFICATION_URI;
@@ -1096,8 +1224,8 @@ public class NotificationManagerService extends INotificationManager.Stub
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0
|| convertSoundToVibration;
-
- if ((useDefaultVibrate || notification.vibrate != null)
+ if (!(inQuietHours && mQuietHoursStill)
+ && (useDefaultVibrate || notification.vibrate != null)
&& !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {
mVibrateNotification = r;
@@ -1131,6 +1259,21 @@ public class NotificationManagerService extends INotificationManager.Stub
idOut[0] = id;
}
+ private boolean inQuietHours() {
+ if (mQuietHoursEnabled && (mQuietHoursStart != mQuietHoursEnd)) {
+ // Get the date in "quiet hours" format.
+ Calendar calendar = Calendar.getInstance();
+ int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE);
+ if (mQuietHoursEnd < mQuietHoursStart) {
+ // Starts at night, ends in the morning.
+ return (minutes > mQuietHoursStart) || (minutes < mQuietHoursEnd);
+ } else {
+ return (minutes > mQuietHoursStart) && (minutes < mQuietHoursEnd);
+ }
+ }
+ return false;
+ }
+
private void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
if (!manager.isEnabled()) {
@@ -1376,17 +1519,45 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- // Don't flash while we are in a call or screen is on
- if (mLedNotification == null || mInCall || mScreenOn) {
+ boolean wasScreenOn = mWasScreenOn;
+ mWasScreenOn = false;
+
+ if (mLedNotification == null) {
+ mNotificationLight.turnOff();
+ return;
+ }
+
+ // We can assume that if the user turned the screen off while there was
+ // still an active notification then they wanted to keep the notification
+ // for later. In this case we shouldn't flash the notification light.
+ // For special notifications that automatically turn the screen on (such
+ // as missed calls), we use this flag to force the notification light
+ // even if the screen was turned off.
+ boolean forceWithScreenOff = (mLedNotification.notification.flags &
+ Notification.FLAG_FORCE_LED_SCREEN_OFF) != 0;
+
+ // Don't flash while we are in a call, screen is on or we are in quiet hours with light dimmed
+ if (mInCall || mScreenOn || (inQuietHours() && mQuietHoursDim) || (wasScreenOn && !forceWithScreenOff)) {
mNotificationLight.turnOff();
} else {
- int ledARGB = mLedNotification.notification.ledARGB;
- int ledOnMS = mLedNotification.notification.ledOnMS;
- int ledOffMS = mLedNotification.notification.ledOffMS;
- if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
- ledARGB = mDefaultNotificationColor;
- ledOnMS = mDefaultNotificationLedOn;
- ledOffMS = mDefaultNotificationLedOff;
+ int ledARGB;
+ int ledOnMS;
+ int ledOffMS;
+ NotificationLedValues ledValues = getLedValuesForNotification(mLedNotification);
+ if (ledValues != null) {
+ ledARGB = ledValues.color != 0 ? ledValues.color : mDefaultNotificationColor;
+ ledOnMS = ledValues.onMS >= 0 ? ledValues.onMS : mDefaultNotificationLedOn;
+ ledOffMS = ledValues.offMS >= 0 ? ledValues.offMS : mDefaultNotificationLedOn;
+ } else {
+ if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
+ ledARGB = mDefaultNotificationColor;
+ ledOnMS = mDefaultNotificationLedOn;
+ ledOffMS = mDefaultNotificationLedOff;
+ } else {
+ ledARGB = mLedNotification.notification.ledARGB;
+ ledOnMS = mLedNotification.notification.ledOnMS;
+ ledOffMS = mLedNotification.notification.ledOffMS;
+ }
}
if (mNotificationPulseEnabled) {
// pulse repeatedly
@@ -1396,6 +1567,47 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
+ private void parseNotificationPulseCustomValuesString(String customLedValuesString) {
+ if (TextUtils.isEmpty(customLedValuesString)) {
+ return;
+ }
+
+ for (String packageValuesString : customLedValuesString.split("\\|")) {
+ String[] packageValues = packageValuesString.split("=");
+ if (packageValues.length != 2) {
+ Log.e(TAG, "Error parsing custom led values for unknown package");
+ continue;
+ }
+ String packageName = packageValues[0];
+ String[] values = packageValues[1].split(";");
+ if (values.length != 3) {
+ Log.e(TAG, "Error parsing custom led values '" + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ NotificationLedValues ledValues = new NotificationLedValues();
+ try {
+ ledValues.color = Integer.parseInt(values[0]);
+ ledValues.onMS = Integer.parseInt(values[1]);
+ ledValues.offMS = Integer.parseInt(values[2]);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing custom led values '" + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ mNotificationPulseCustomLedValues.put(packageName, ledValues);
+ }
+ }
+
+ private NotificationLedValues getLedValuesForNotification(NotificationRecord ledNotification) {
+ return mNotificationPulseCustomLedValues.get(mapPackage(ledNotification.pkg));
+ }
+
+ private String mapPackage(String pkg) {
+ if(!mPackageNameMappings.containsKey(pkg)) {
+ return pkg;
+ }
+ return mPackageNameMappings.get(pkg);
+ }
+
// lock on mNotificationList
private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
diff --git a/services/java/com/android/server/ProfileManagerService.java b/services/java/com/android/server/ProfileManagerService.java
new file mode 100644
index 0000000..aa769a5
--- /dev/null
+++ b/services/java/com/android/server/ProfileManagerService.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2011, 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;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.app.IProfileManager;
+import android.app.NotificationGroup;
+import android.app.Profile;
+import android.app.ProfileGroup;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.os.ParcelUuid;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/** {@hide} */
+public class ProfileManagerService extends IProfileManager.Stub {
+ // Enable the below for detailed logging of this class
+ private static final boolean LOCAL_LOGV = false;
+ /**
+ * <p>Broadcast Action: A new profile has been selected. This can be triggered by the user
+ * or by calls to the ProfileManagerService / Profile.</p>
+ * @hide
+ */
+ public static final String INTENT_ACTION_PROFILE_SELECTED = "android.intent.action.PROFILE_SELECTED";
+
+ public static final String PERMISSION_CHANGE_SETTINGS = "android.permission.WRITE_SETTINGS";
+
+ private static final String PROFILE_FILENAME = "/data/system/profiles.xml";
+
+ private static final String TAG = "ProfileService";
+
+ private Map<UUID, Profile> mProfiles;
+
+ // Match UUIDs and names, used for reverse compatibility
+ private Map<String, UUID> mProfileNames;
+
+ private Map<UUID, NotificationGroup> mGroups;
+
+ private Profile mActiveProfile;
+
+ // Well-known UUID of the wildcard group
+ private static final UUID mWildcardUUID = UUID.fromString("a126d48a-aaef-47c4-baed-7f0e44aeffe5");
+ private NotificationGroup mWildcardGroup;
+
+ private Context mContext;
+ private boolean mDirty;
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(Intent.ACTION_LOCALE_CHANGED)) {
+ persistIfDirty();
+ initialize();
+ } else if (action.equals(Intent.ACTION_SHUTDOWN)) {
+ persistIfDirty();
+ }
+ }
+ };
+
+ public ProfileManagerService(Context context) {
+ mContext = context;
+
+ mWildcardGroup = new NotificationGroup(
+ context.getString(com.android.internal.R.string.wildcardProfile),
+ com.android.internal.R.string.wildcardProfile,
+ mWildcardUUID);
+
+ initialize();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_LOCALE_CHANGED);
+ filter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiver(mIntentReceiver, filter);
+ }
+
+ private void initialize() {
+ initialize(false);
+ }
+
+ private void initialize(boolean skipFile) {
+ mProfiles = new HashMap<UUID, Profile>();
+ mProfileNames = new HashMap<String, UUID>();
+ mGroups = new HashMap<UUID, NotificationGroup>();
+ mDirty = false;
+
+ boolean init = skipFile;
+
+ if (! skipFile) {
+ try {
+ loadFromFile();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (XmlPullParserException e) {
+ init = true;
+ } catch (IOException e) {
+ init = true;
+ }
+ }
+
+ if (init) {
+ try {
+ initialiseStructure();
+ } catch (Throwable ex) {
+ Log.e(TAG, "Error loading xml from resource: ", ex);
+ }
+ }
+ }
+
+ @Override
+ public void resetAll() {
+ enforceChangePermissions();
+ initialize(true);
+ }
+
+ @Override
+ @Deprecated
+ public boolean setActiveProfileByName(String profileName) throws RemoteException, SecurityException {
+ if (mProfileNames.containsKey(profileName)) {
+ if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(String) found profile name in mProfileNames.");
+ return setActiveProfile(mProfiles.get(mProfileNames.get(profileName)), true);
+ } else {
+ // Since profileName could not be casted into a UUID, we can call it a string.
+ Log.w(TAG, "Unable to find profile to set active, based on string: " + profileName);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setActiveProfile(ParcelUuid profileParcelUuid) throws RemoteException, SecurityException {
+ UUID profileUuid = profileParcelUuid.getUuid();
+ if(mProfiles.containsKey(profileUuid)){
+ if (LOCAL_LOGV) Log.v(TAG, "setActiveProfileByUuid(ParcelUuid) found profile UUID in mProfileNames.");
+ return setActiveProfile(mProfiles.get(profileUuid), true);
+ } else {
+ Log.e(TAG, "Cannot set active profile to: " + profileUuid.toString() + " - does not exist.");
+ return false;
+ }
+ }
+
+ private boolean setActiveProfile(UUID profileUuid, boolean doinit) throws RemoteException {
+ if(mProfiles.containsKey(profileUuid)){
+ if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(UUID, boolean) found profile UUID in mProfiles.");
+ return setActiveProfile(mProfiles.get(profileUuid), doinit);
+ } else {
+ Log.e(TAG, "Cannot set active profile to: " + profileUuid.toString() + " - does not exist.");
+ return false;
+ }
+ }
+
+ private boolean setActiveProfile(Profile newActiveProfile, boolean doinit) throws RemoteException {
+ /*
+ * NOTE: Since this is not a public function, and all public functions
+ * take either a string or a UUID, the active profile should always be
+ * in the collection. If writing another setActiveProfile which receives
+ * a Profile object, run enforceChangePermissions, add the profile to the
+ * list, and THEN add it.
+ */
+
+ try {
+ enforceChangePermissions();
+ Log.d(TAG, "Set active profile to: " + newActiveProfile.getUuid().toString() + " - " + newActiveProfile.getName());
+ Profile lastProfile = mActiveProfile;
+ mActiveProfile = newActiveProfile;
+ mDirty = true;
+ if (doinit) {
+ if (LOCAL_LOGV) Log.v(TAG, "setActiveProfile(Profile, boolean) - Running init");
+
+ /*
+ * We need to clear the caller's identity in order to
+ * - allow the profile switch to execute actions not included in the caller's permissions
+ * - broadcast INTENT_ACTION_PROFILE_SELECTED
+ */
+ long token = clearCallingIdentity();
+
+ // Call profile's "doSelect"
+ mActiveProfile.doSelect(mContext);
+
+ // Notify other applications of newly selected profile.
+ Intent broadcast = new Intent(INTENT_ACTION_PROFILE_SELECTED);
+ broadcast.putExtra("name", mActiveProfile.getName());
+ broadcast.putExtra("uuid", mActiveProfile.getUuid().toString());
+ broadcast.putExtra("lastName", lastProfile.getName());
+ broadcast.putExtra("lastUuid", lastProfile.getUuid().toString());
+ mContext.sendBroadcast(broadcast);
+
+ restoreCallingIdentity(token);
+ persistIfDirty();
+ }
+ return true;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean addProfile(Profile profile) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ addProfileInternal(profile);
+ persistIfDirty();
+ return true;
+ }
+
+ private void addProfileInternal(Profile profile) {
+ // Make sure this profile has all of the correct groups.
+ for (NotificationGroup group : mGroups.values()) {
+ ensureGroupInProfile(profile, group, false);
+ }
+ ensureGroupInProfile(profile, mWildcardGroup, true);
+ mProfiles.put(profile.getUuid(), profile);
+ mProfileNames.put(profile.getName(), profile.getUuid());
+ mDirty = true;
+ }
+
+ private void ensureGroupInProfile(Profile profile, NotificationGroup group, boolean defaultGroup) {
+ if (profile.getProfileGroup(group.getUuid()) != null) {
+ return;
+ }
+
+ /* enforce a matchup between profile and notification group, which not only
+ * works by UUID, but also by name for backwards compatibility */
+ for (ProfileGroup pg : profile.getProfileGroups()) {
+ if (pg.matches(group, defaultGroup)) {
+ return;
+ }
+ }
+
+ /* didn't find any, create new group */
+ profile.addProfileGroup(new ProfileGroup(group.getUuid(), defaultGroup));
+ }
+
+ @Override
+ @Deprecated
+ public Profile getProfileByName(String profileName) throws RemoteException {
+ if (mProfileNames.containsKey(profileName)) {
+ return mProfiles.get(mProfileNames.get(profileName));
+ } else if (mProfiles.containsKey(UUID.fromString((profileName)))) {
+ return mProfiles.get(UUID.fromString(profileName));
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Profile getProfile(ParcelUuid profileParcelUuid) {
+ UUID profileUuid = profileParcelUuid.getUuid();
+ return getProfile(profileUuid);
+ }
+
+ public Profile getProfile(UUID profileUuid) {
+ // use primary UUID first
+ if (mProfiles.containsKey(profileUuid)) {
+ return mProfiles.get(profileUuid);
+ }
+ // if no match was found: try secondary UUID
+ for (Profile p : mProfiles.values()) {
+ for (UUID uuid : p.getSecondaryUuids()) {
+ if (profileUuid.equals(uuid)) {
+ return p;
+ }
+ }
+ }
+ // nothing found
+ return null;
+ }
+
+ @Override
+ public Profile[] getProfiles() throws RemoteException {
+ Profile[] tmpArr = mProfiles.values().toArray(new Profile[mProfiles.size()]);
+ Arrays.sort(tmpArr);
+ return tmpArr;
+ }
+
+ @Override
+ public Profile getActiveProfile() throws RemoteException {
+ return mActiveProfile;
+ }
+
+ @Override
+ public boolean removeProfile(Profile profile) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ if (mProfileNames.remove(profile.getName()) != null && mProfiles.remove(profile.getUuid()) != null) {
+ mDirty = true;
+ persistIfDirty();
+ return true;
+ } else{
+ return false;
+ }
+ }
+
+ @Override
+ public void updateProfile(Profile profile) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ Profile old = mProfiles.get(profile.getUuid());
+ if (old != null) {
+ mProfileNames.remove(old.getName());
+ mProfileNames.put(profile.getName(), profile.getUuid());
+ mProfiles.put(profile.getUuid(), profile);
+ /* no need to set mDirty, if the profile was actually changed,
+ * it's marked as dirty by itself */
+ persistIfDirty();
+
+ // Also update we changed the active profile
+ if (mActiveProfile != null && mActiveProfile.getUuid().equals(profile.getUuid())) {
+ setActiveProfile(profile, true);
+ }
+ }
+ }
+
+ @Override
+ public boolean profileExists(ParcelUuid profileUuid) throws RemoteException {
+ return mProfiles.containsKey(profileUuid.getUuid());
+ }
+
+ @Override
+ public boolean profileExistsByName(String profileName) throws RemoteException {
+ for (Map.Entry<String, UUID> entry : mProfileNames.entrySet()) {
+ if (entry.getKey().equalsIgnoreCase(profileName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean notificationGroupExistsByName(String notificationGroupName) throws RemoteException {
+ for (NotificationGroup group : mGroups.values()) {
+ if (group.getName().equalsIgnoreCase(notificationGroupName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public NotificationGroup[] getNotificationGroups() throws RemoteException {
+ return mGroups.values().toArray(new NotificationGroup[mGroups.size()]);
+ }
+
+ @Override
+ public void addNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ addNotificationGroupInternal(group);
+ persistIfDirty();
+ }
+
+ private void addNotificationGroupInternal(NotificationGroup group) {
+ if (mGroups.put(group.getUuid(), group) == null) {
+ // If the above is true, then the ProfileGroup shouldn't exist in
+ // the profile. Ensure it is added.
+ for (Profile profile : mProfiles.values()) {
+ ensureGroupInProfile(profile, group, false);
+ }
+ }
+ mDirty = true;
+ }
+
+ @Override
+ public void removeNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ mDirty |= (mGroups.remove(group.getUuid()) != null);
+ // Remove the corresponding ProfileGroup from all the profiles too if
+ // they use it.
+ for (Profile profile : mProfiles.values()) {
+ profile.removeProfileGroup(group.getUuid());
+ }
+ persistIfDirty();
+ }
+
+ @Override
+ public void updateNotificationGroup(NotificationGroup group) throws RemoteException, SecurityException {
+ enforceChangePermissions();
+ NotificationGroup old = mGroups.get(group.getUuid());
+ if (old != null) {
+ mGroups.put(group.getUuid(), group);
+ /* no need to set mDirty, if the group was actually changed,
+ * it's marked as dirty by itself */
+ persistIfDirty();
+ }
+ }
+
+ @Override
+ public NotificationGroup getNotificationGroupForPackage(String pkg) throws RemoteException {
+ for (NotificationGroup group : mGroups.values()) {
+ if (group.hasPackage(pkg)) {
+ return group;
+ }
+ }
+ return null;
+ }
+
+ private void loadFromFile() throws RemoteException, XmlPullParserException, IOException {
+ XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();
+ XmlPullParser xpp = xppf.newPullParser();
+ FileReader fr = new FileReader(PROFILE_FILENAME);
+ xpp.setInput(fr);
+ loadXml(xpp, mContext);
+ fr.close();
+ persistIfDirty();
+ }
+
+ private void loadXml(XmlPullParser xpp, Context context) throws
+ XmlPullParserException, IOException, RemoteException {
+ int event = xpp.next();
+ String active = null;
+ while (event != XmlPullParser.END_TAG || !"profiles".equals(xpp.getName())) {
+ if (event == XmlPullParser.START_TAG) {
+ String name = xpp.getName();
+ if (name.equals("active")) {
+ active = xpp.nextText();
+ Log.d(TAG, "Found active: " + active);
+ } else if (name.equals("profile")) {
+ Profile prof = Profile.fromXml(xpp, context);
+ addProfileInternal(prof);
+ // Failsafe if no active found
+ if (active == null) {
+ active = prof.getUuid().toString();
+ }
+ } else if (name.equals("notificationGroup")) {
+ NotificationGroup ng = NotificationGroup.fromXml(xpp, context);
+ addNotificationGroupInternal(ng);
+ }
+ } else if (event == XmlPullParser.END_DOCUMENT) {
+ throw new IOException("Premature end of file while reading " + PROFILE_FILENAME);
+ }
+ event = xpp.next();
+ }
+ // Don't do initialisation on startup. The AudioManager doesn't exist yet
+ // and besides, the volume settings will have survived the reboot.
+ try {
+ // Try / catch block to detect if XML file needs to be upgraded.
+ setActiveProfile(UUID.fromString(active), false);
+ } catch (IllegalArgumentException e) {
+ if (mProfileNames.containsKey(active)) {
+ setActiveProfile(mProfileNames.get(active), false);
+ } else {
+ // Final fail-safe: We must have SOME profile active.
+ // If we couldn't select one by now, we'll pick the first in the set.
+ setActiveProfile(mProfiles.values().iterator().next(), false);
+ }
+ // This is a hint that we probably just upgraded the XML file. Save changes.
+ mDirty = true;
+ }
+ }
+
+ private void initialiseStructure() throws RemoteException, XmlPullParserException, IOException {
+ XmlResourceParser xml = mContext.getResources().getXml(
+ com.android.internal.R.xml.profile_default);
+ try {
+ loadXml(xml, mContext);
+ mDirty = true;
+ persistIfDirty();
+ } finally {
+ xml.close();
+ }
+ }
+
+ private String getXmlString() throws RemoteException {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<profiles>\n<active>");
+ builder.append(TextUtils.htmlEncode(getActiveProfile().getUuid().toString()));
+ builder.append("</active>\n");
+
+ for (Profile p : mProfiles.values()) {
+ p.getXmlString(builder, mContext);
+ }
+ for (NotificationGroup g : mGroups.values()) {
+ g.getXmlString(builder, mContext);
+ }
+ builder.append("</profiles>\n");
+ return builder.toString();
+ }
+
+ @Override
+ public NotificationGroup getNotificationGroup(ParcelUuid uuid) throws RemoteException {
+ if (uuid.getUuid().equals(mWildcardGroup.getUuid())) {
+ return mWildcardGroup;
+ }
+ return mGroups.get(uuid.getUuid());
+ }
+
+ private synchronized void persistIfDirty() {
+ boolean dirty = mDirty;
+ if (!dirty) {
+ for (Profile profile : mProfiles.values()) {
+ if (profile.isDirty()) {
+ dirty = true;
+ break;
+ }
+ }
+ }
+ if (!dirty) {
+ for (NotificationGroup group : mGroups.values()) {
+ if (group.isDirty()) {
+ dirty = true;
+ break;
+ }
+ }
+ }
+ if (dirty) {
+ try {
+ Log.d(TAG, "Saving profile data...");
+ FileWriter fw = new FileWriter(PROFILE_FILENAME);
+ fw.write(getXmlString());
+ fw.close();
+ Log.d(TAG, "Save completed.");
+ mDirty = false;
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void enforceChangePermissions() throws SecurityException {
+ mContext.enforceCallingOrSelfPermission(PERMISSION_CHANGE_SETTINGS,
+ "You do not have permissions to change the Profile Manager.");
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 894c4d0..3dd22b0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -346,6 +346,7 @@ class ServerThread extends Thread {
StatusBarManagerService statusBar = null;
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
+ ProfileManagerService profile = null;
NotificationManagerService notification = null;
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
@@ -553,6 +554,14 @@ class ServerThread extends Thread {
}
try {
+ Slog.i(TAG, "Profile Manager");
+ profile = new ProfileManagerService(context);
+ ServiceManager.addService(Context.PROFILE_SERVICE, profile);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Profile Manager", e);
+ }
+
+ try {
Slog.i(TAG, "Notification Manager");
notification = new NotificationManagerService(context, statusBar, lights);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index df91dec..64cae1d 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -39,6 +39,7 @@ import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
import android.view.InputDevice;
+import java.util.Calendar;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
@@ -164,6 +165,29 @@ public class VibratorService extends IVibratorService.Stub
return doVibratorExists();
}
+ private boolean inQuietHours() {
+ boolean quietHoursEnabled = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.QUIET_HOURS_ENABLED, 0) != 0;
+ int quietHoursStart = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.QUIET_HOURS_START, 0);
+ int quietHoursEnd = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.QUIET_HOURS_END, 0);
+ boolean quietHoursHaptic = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.QUIET_HOURS_HAPTIC, 0) != 0;
+ if (quietHoursEnabled && quietHoursHaptic && (quietHoursStart != quietHoursEnd)) {
+ // Get the date in "quiet hours" format.
+ Calendar calendar = Calendar.getInstance();
+ int minutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE);
+ if (quietHoursEnd < quietHoursStart) {
+ // Starts at night, ends in the morning.
+ return (minutes > quietHoursStart) || (minutes < quietHoursEnd);
+ } else {
+ return (minutes > quietHoursStart) && (minutes < quietHoursEnd);
+ }
+ }
+ return false;
+ }
+
public void vibrate(long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -174,7 +198,7 @@ public class VibratorService extends IVibratorService.Stub
// timeout of 0 or negative. This will ensure that a vibration has
// either a timeout of > 0 or a non-null pattern.
if (milliseconds <= 0 || (mCurrentVibration != null
- && mCurrentVibration.hasLongerTimeout(milliseconds))) {
+ && mCurrentVibration.hasLongerTimeout(milliseconds)) || inQuietHours()) {
// Ignore this vibration since the current vibration will play for
// longer than milliseconds.
return;
@@ -204,6 +228,9 @@ public class VibratorService extends IVibratorService.Stub
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ if (inQuietHours()) {
+ return;
+ }
int uid = Binder.getCallingUid();
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index c7f7390..1aa1fb0 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -47,6 +47,7 @@ import com.android.internal.telephony.ITelephony;
import android.util.Log;
import android.view.WindowManager;
+import android.view.KeyEvent;
public final class ShutdownThread extends Thread {
// constants
@@ -129,22 +130,63 @@ public final class ShutdownThread extends Thread {
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
- sConfirmDialog = new AlertDialog.Builder(context)
- .setTitle(mRebootSafeMode
- ? com.android.internal.R.string.reboot_safemode_title
- : com.android.internal.R.string.power_off)
- .setMessage(resourceId)
- .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- beginShutdownSequence(context);
- }
- })
- .setNegativeButton(com.android.internal.R.string.no, null)
- .create();
+ if (mReboot && !mRebootSafeMode){
+ sConfirmDialog = new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(com.android.internal.R.string.reboot_system)
+ .setSingleChoiceItems(com.android.internal.R.array.shutdown_reboot_options, 0, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which < 0)
+ return;
+
+ String actions[] = context.getResources().getStringArray(com.android.internal.R.array.shutdown_reboot_actions);
+
+ if (actions != null && which < actions.length)
+ mRebootReason = actions[which];
+ }
+ })
+ .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mReboot = true;
+ beginShutdownSequence(context);
+ }
+ })
+ .setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mReboot = false;
+ dialog.cancel();
+ }
+ })
+ .create();
+ sConfirmDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
+ public boolean onKey (DialogInterface dialog, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ mReboot = false;
+ dialog.cancel();
+ }
+ return true;
+ }
+ });
+ } else {
+ sConfirmDialog = new AlertDialog.Builder(context)
+ .setTitle(mRebootSafeMode
+ ? com.android.internal.R.string.reboot_safemode_title
+ : com.android.internal.R.string.power_off)
+ .setMessage(resourceId)
+ .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ beginShutdownSequence(context);
+ }
+ })
+ .setNegativeButton(com.android.internal.R.string.no, null)
+ .create();
+ }
+
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
+
} else {
beginShutdownSequence(context);
}
@@ -213,8 +255,13 @@ public final class ShutdownThread extends Thread {
// throw up an indeterminate system dialog to indicate radio is
// shutting down.
ProgressDialog pd = new ProgressDialog(context);
- pd.setTitle(context.getText(com.android.internal.R.string.power_off));
- pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ if (mReboot) {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_system));
+ pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress));
+ } else {
+ pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ }
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -294,7 +341,7 @@ public final class ShutdownThread extends Thread {
}
Log.i(TAG, "Sending shutdown broadcast...");
-
+
// First send the high-level shut down broadcast.
mActionDone = false;
mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
@@ -314,9 +361,9 @@ public final class ShutdownThread extends Thread {
}
}
}
-
+
Log.i(TAG, "Shutting down activity manager...");
-
+
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 51edb44..38233d0 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5430,6 +5430,12 @@ public class WindowManagerService extends IWindowManager.Stub
mInputManager.setInputFilter(filter);
}
+ // Called by window manager policy. Not exposed externally.
+ @Override
+ public void reboot() {
+ ShutdownThread.reboot(mContext, null, true);
+ }
+
public void setCurrentUser(final int newUserId) {
synchronized (mWindowMap) {
mCurrentUserId = newUserId;