diff options
author | John Spurlock <jspurlock@google.com> | 2015-02-11 19:04:11 -0500 |
---|---|---|
committer | John Spurlock <jspurlock@google.com> | 2015-02-12 22:48:37 -0500 |
commit | cdb57aeb0e2c83a887c86da0ca2a890df7f02f41 (patch) | |
tree | cd58462862527b7eb432d1749e5752745f1b8889 | |
parent | ad680d46be19cbee16d42cbed4d2ed250648ac0b (diff) | |
download | frameworks_base-cdb57aeb0e2c83a887c86da0ca2a890df7f02f41.zip frameworks_base-cdb57aeb0e2c83a887c86da0ca2a890df7f02f41.tar.gz frameworks_base-cdb57aeb0e2c83a887c86da0ca2a890df7f02f41.tar.bz2 |
Allow sysui-managed remote volume controllers.
- Relax restriction on audio service calls that assume the volume
ui is systemui, allow calls from a blessed component app.
- Blessed component app service saved in secure settings.
- SystemUI mediates requests to replace the volume dialog, prompts
the user on activation.
- Show a low pri ongoing notification when the volume dialog is
being replaced, to allow user restoration at any time.
- Replace the controller management code in VolumeUI to use a
ServiceMonitor, backed by the new blessed app component setting.
- Add proper zen-related noman client wrappers, make avail to the
registered volume controller.
- Everything is still @hidden, no api impact.
Bug: 19260237
Change-Id: Ie1383f57659090318a7eda737fdad5b8f88737d4
11 files changed, 322 insertions, 70 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 88b9080..5d864df 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -75,6 +75,7 @@ interface INotificationManager ZenModeConfig getZenModeConfig(); boolean setZenModeConfig(in ZenModeConfig config); + oneway void setZenMode(int mode); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); oneway void setZenModeCondition(in Condition condition); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index cf54107..479327d 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -27,6 +27,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; +import android.service.notification.Condition; +import android.service.notification.IConditionListener; +import android.service.notification.ZenModeConfig; import android.util.Log; /** @@ -276,5 +279,53 @@ public class NotificationManager } } + /** + * @hide + */ + public void setZenMode(int mode) { + INotificationManager service = getService(); + try { + service.setZenMode(mode); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void requestZenModeConditions(IConditionListener listener, int relevance) { + INotificationManager service = getService(); + try { + service.requestZenModeConditions(listener, relevance); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void setZenModeCondition(Condition exitCondition) { + INotificationManager service = getService(); + try { + service.setZenModeCondition(exitCondition); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public Condition getZenModeCondition() { + INotificationManager service = getService(); + try { + final ZenModeConfig config = service.getZenModeConfig(); + if (config != null) { + return config.exitCondition; + } + } catch (RemoteException e) { + } + return null; + } + private Context mContext; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dddbe78..5380cd6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4762,6 +4762,10 @@ public final class Settings { public static final String BAR_SERVICE_COMPONENT = "bar_service_component"; /** @hide */ + public static final String VOLUME_CONTROLLER_SERVICE_COMPONENT + = "volume_controller_service_component"; + + /** @hide */ public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; /** diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index 873c142..ef5710c 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -15,8 +15,6 @@ */ package android.media; -import android.os.IBinder; - import com.android.server.LocalServices; /** @@ -47,6 +45,8 @@ public abstract class AudioManagerInternal { public abstract void setRingerModeInternal(int ringerMode, String caller); + public abstract int getVolumeControllerUid(); + public interface RingerModeDelegate { /** Called when external ringer mode is evaluated, returns the new internal ringer mode */ int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller, diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index aa53a3e..3fc75d2 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -40,6 +40,7 @@ <item type="id" name="notification_power"/> <item type="id" name="notification_screenshot"/> <item type="id" name="notification_hidden"/> + <item type="id" name="notification_volumeui"/> <!-- Whether the icon is from a notification for which targetSdk < L --> <item type="id" name="icon_is_pre_L"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6afca8a..0420d35 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -932,4 +932,19 @@ <!-- Hide quick settings tile confirmation button --> <string name="quick_settings_reset_confirmation_button">Hide</string> + + <!-- VolumeUI activation dialog: warning message --> + <string name="volumeui_prompt_message"><xliff:g id="app_name" example="Volume Prototype 1">%1$s</xliff:g> wants to be the volume dialog.</string> + + <!-- VolumeUI activation dialog: allow button label --> + <string name="volumeui_prompt_allow">Allow</string> + + <!-- VolumeUI activation dialog: deny button label --> + <string name="volumeui_prompt_deny">Deny</string> + + <!-- VolumeUI restoration notification: title --> + <string name="volumeui_notification_title"><xliff:g id="app_name" example="Volume Prototype 1">%1$s</xliff:g> is the volume dialog</string> + + <!-- VolumeUI restoration notification: text --> + <string name="volumeui_notification_text">Touch to restore the original.</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java index aea9ec6..69a4932 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ServiceMonitor.java @@ -279,4 +279,14 @@ public class ServiceMonitor { } return sb.append('}').toString(); } + + public ComponentName getComponent() { + return getComponentNameFromSetting(); + } + + public void setComponent(ComponentName component) { + final String setting = component == null ? null : component.flattenToShortString(); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + mSettingKey, setting, UserHandle.USER_CURRENT); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index dbdb578..bea0c86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.policy; import android.app.AlarmManager; -import android.app.INotificationManager; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -28,8 +27,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -53,7 +50,7 @@ public class ZenModeControllerImpl implements ZenModeController { private final Context mContext; private final GlobalSetting mModeSetting; private final GlobalSetting mConfigSetting; - private final INotificationManager mNoMan; + private final NotificationManager mNoMan; private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>(); private final AlarmManager mAlarmManager; private final SetupObserver mSetupObserver; @@ -78,8 +75,7 @@ public class ZenModeControllerImpl implements ZenModeController { }; mModeSetting.setListening(true); mConfigSetting.setListening(true); - mNoMan = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mSetupObserver = new SetupObserver(handler); mSetupObserver.register(); @@ -113,11 +109,7 @@ public class ZenModeControllerImpl implements ZenModeController { @Override public void requestConditions(boolean request) { mRequesting = request; - try { - mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0); - } catch (RemoteException e) { - // noop - } + mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0); if (!mRequesting) { mConditions.clear(); } @@ -125,24 +117,12 @@ public class ZenModeControllerImpl implements ZenModeController { @Override public void setExitCondition(Condition exitCondition) { - try { - mNoMan.setZenModeCondition(exitCondition); - } catch (RemoteException e) { - // noop - } + mNoMan.setZenModeCondition(exitCondition); } @Override public Condition getExitCondition() { - try { - final ZenModeConfig config = mNoMan.getZenModeConfig(); - if (config != null) { - return config.exitCondition; - } - } catch (RemoteException e) { - // noop - } - return null; + return mNoMan.getZenModeCondition(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 7102c2a..8048a48 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -1,60 +1,73 @@ +/* + * Copyright (C) 2014 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.systemui.volume; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; -import android.database.ContentObserver; import android.media.AudioManager; import android.media.IRemoteVolumeController; import android.media.IVolumeController; import android.media.session.ISessionController; import android.media.session.MediaController; import android.media.session.MediaSessionManager; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.statusbar.ServiceMonitor; import com.android.systemui.statusbar.phone.PhoneStatusBar; +import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import java.io.FileDescriptor; import java.io.PrintWriter; -/* - * Copyright (C) 2014 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. - */ - public class VolumeUI extends SystemUI { private static final String TAG = "VolumeUI"; - private static final String SETTING = "systemui_volume_controller"; // for testing - private static final Uri SETTING_URI = Settings.Global.getUriFor(SETTING); - private static final int DEFAULT = 1; // enabled by default + private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); private final Handler mHandler = new Handler(); + private final Receiver mReceiver = new Receiver(); + private final RestorationNotification mRestorationNotification = new RestorationNotification(); private boolean mEnabled; private AudioManager mAudioManager; + private NotificationManager mNotificationManager; private MediaSessionManager mMediaSessionManager; private VolumeController mVolumeController; private RemoteVolumeController mRemoteVolumeController; + private ServiceMonitor mVolumeControllerService; private VolumePanel mPanel; private int mDismissDelay; @@ -64,14 +77,19 @@ public class VolumeUI extends SystemUI { mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui); if (!mEnabled) return; mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mNotificationManager = + (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mMediaSessionManager = (MediaSessionManager) mContext .getSystemService(Context.MEDIA_SESSION_SERVICE); initPanel(); mVolumeController = new VolumeController(); mRemoteVolumeController = new RemoteVolumeController(); putComponent(VolumeComponent.class, mVolumeController); - updateController(); - mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver); + mReceiver.start(); + mVolumeControllerService = new ServiceMonitor(TAG, LOGD, + mContext, Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT, + new ServiceMonitorCallbacks()); + mVolumeControllerService.start(); } @Override @@ -85,18 +103,19 @@ public class VolumeUI extends SystemUI { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("mEnabled="); pw.println(mEnabled); + pw.print("mVolumeControllerService="); pw.println(mVolumeControllerService.getComponent()); if (mPanel != null) { mPanel.dump(fd, pw, args); } } - private void updateController() { - if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) { - Log.d(TAG, "Registering volume controller"); + private void setVolumeController(boolean register) { + if (register) { + if (LOGD) Log.d(TAG, "Registering volume controller"); mAudioManager.setVolumeController(mVolumeController); mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController); } else { - Log.d(TAG, "Unregistering volume controller"); + if (LOGD) Log.d(TAG, "Unregistering volume controller"); mAudioManager.setVolumeController(null); mMediaSessionManager.setRemoteVolumeController(null); } @@ -129,13 +148,32 @@ public class VolumeUI extends SystemUI { }); } - private final ContentObserver mObserver = new ContentObserver(mHandler) { - public void onChange(boolean selfChange, Uri uri) { - if (SETTING_URI.equals(uri)) { - updateController(); + private String getAppLabel(ComponentName component) { + final String pkg = component.getPackageName(); + try { + final ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(pkg, 0); + final String rt = mContext.getPackageManager().getApplicationLabel(ai).toString(); + if (!TextUtils.isEmpty(rt)) { + return rt; } + } catch (Exception e) { + Log.w(TAG, "Error loading app label", e); } - }; + return pkg; + } + + private void showServiceActivationDialog(final ComponentName component) { + final SystemUIDialog d = new SystemUIDialog(mContext); + d.setMessage(mContext.getString(R.string.volumeui_prompt_message, getAppLabel(component))); + d.setPositiveButton(R.string.volumeui_prompt_allow, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mVolumeControllerService.setComponent(component); + } + }); + d.setNegativeButton(R.string.volumeui_prompt_deny, null); + d.show(); + } private final Runnable mStartZenSettings = new Runnable() { @Override @@ -213,4 +251,83 @@ public class VolumeUI extends SystemUI { // than by remoteVolumeChanged. } } + + private final class ServiceMonitorCallbacks implements ServiceMonitor.Callbacks { + @Override + public void onNoService() { + if (LOGD) Log.d(TAG, "onNoService"); + setVolumeController(true); + mRestorationNotification.hide(); + } + + @Override + public long onServiceStartAttempt() { + if (LOGD) Log.d(TAG, "onServiceStartAttempt"); + setVolumeController(false); + mVolumeController.dismissNow(); + mRestorationNotification.show(); + return 0; + } + } + + private final class Receiver extends BroadcastReceiver { + private static final String ENABLE = "com.android.systemui.vui.ENABLE"; + private static final String DISABLE = "com.android.systemui.vui.DISABLE"; + private static final String EXTRA_COMPONENT = "component"; + + public void start() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(ENABLE); + filter.addAction(DISABLE); + mContext.registerReceiver(this, filter, null, mHandler); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT); + final boolean current = component.equals(mVolumeControllerService.getComponent()); + if (ENABLE.equals(action) && component != null) { + if (!current) { + showServiceActivationDialog(component); + } + } + if (DISABLE.equals(action) && component != null) { + if (current) { + mVolumeControllerService.setComponent(null); + } + } + } + } + + private final class RestorationNotification { + public void hide() { + mNotificationManager.cancel(R.id.notification_volumeui); + } + + public void show() { + final ComponentName component = mVolumeControllerService.getComponent(); + if (component == null) { + Log.w(TAG, "Not showing restoration notification, component not active"); + return; + } + final Intent intent = new Intent(Receiver.DISABLE) + .putExtra(Receiver.EXTRA_COMPONENT, component); + mNotificationManager.notify(R.id.notification_volumeui, + new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_ringer_audible) + .setWhen(0) + .setShowWhen(false) + .setOngoing(true) + .setContentTitle(mContext.getString( + R.string.volumeui_notification_title, getAppLabel(component))) + .setContentText(mContext.getString(R.string.volumeui_notification_text)) + .setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0)) + .setPriority(Notification.PRIORITY_MIN) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setColor(mContext.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)) + .build()); + } + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index eaece09..4301427 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -182,6 +182,7 @@ public class AudioService extends IAudioService.Stub { /** The controller for the volume UI. */ private final VolumeController mVolumeController = new VolumeController(); + private final ControllerService mControllerService = new ControllerService(); // sendMsg() flags /** If the msg is already queued, replace it with this one. */ @@ -708,6 +709,7 @@ public class AudioService extends IAudioService.Stub { SAFE_VOLUME_CONFIGURE_TIMEOUT_MS); StreamOverride.init(mContext); + mControllerService.init(); } private void createAudioSystemThread() { @@ -1833,7 +1835,7 @@ public class AudioService extends IAudioService.Stub { } public void setRingerModeInternal(int ringerMode, String caller) { - enforceSelfOrSystemUI("setRingerModeInternal"); + enforceVolumeController("setRingerModeInternal"); setRingerMode(ringerMode, caller, false /*external*/); } @@ -5013,7 +5015,7 @@ public class AudioService extends IAudioService.Stub { @Override public void setRemoteStreamVolume(int index) { - enforceSelfOrSystemUI("set the remote stream volume"); + enforceVolumeController("set the remote stream volume"); mMediaFocusControl.setRemoteStreamVolume(index); } @@ -5333,7 +5335,7 @@ public class AudioService extends IAudioService.Stub { @Override public void disableSafeMediaVolume() { - enforceSelfOrSystemUI("disable the safe media volume"); + enforceVolumeController("disable the safe media volume"); synchronized (mSafeMediaVolumeState) { setSafeMediaVolumeEnabled(false); if (mPendingVolumeCommand != null) { @@ -5505,6 +5507,7 @@ public class AudioService extends IAudioService.Stub { pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs); pw.print(" mMcc="); pw.println(mMcc); pw.print(" mHasVibrator="); pw.println(mHasVibrator); + pw.print(" mControllerService="); pw.println(mControllerService); dumpAudioPolicies(pw); } @@ -5528,14 +5531,17 @@ public class AudioService extends IAudioService.Stub { } } - private void enforceSelfOrSystemUI(String action) { + private void enforceVolumeController(String action) { + if (mControllerService.mUid != 0 && Binder.getCallingUid() == mControllerService.mUid) { + return; + } mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, "Only SystemUI can " + action); } @Override public void setVolumeController(final IVolumeController controller) { - enforceSelfOrSystemUI("set the volume controller"); + enforceVolumeController("set the volume controller"); // return early if things are not actually changing if (mVolumeController.isSameBinder(controller)) { @@ -5566,7 +5572,7 @@ public class AudioService extends IAudioService.Stub { @Override public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) { - enforceSelfOrSystemUI("notify about volume controller visibility"); + enforceVolumeController("notify about volume controller visibility"); // return early if the controller is not current if (!mVolumeController.isSameBinder(controller)) { @@ -5751,6 +5757,11 @@ public class AudioService extends IAudioService.Stub { public void setRingerModeInternal(int ringerMode, String caller) { AudioService.this.setRingerModeInternal(ringerMode, caller); } + + @Override + public int getVolumeControllerUid() { + return mControllerService.mUid; + } } //========================================================================================== @@ -5915,4 +5926,42 @@ public class AudioService extends IAudioService.Stub { private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies = new HashMap<IBinder, AudioPolicyProxy>(); private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies + + private class ControllerService extends ContentObserver { + private int mUid; + private ComponentName mComponent; + + public ControllerService() { + super(null); + } + + @Override + public String toString() { + return String.format("{mUid=%d,mComponent=%s}", mUid, mComponent); + } + + public void init() { + onChange(true); + mContentResolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT), false, this); + } + + @Override + public void onChange(boolean selfChange) { + mUid = 0; + mComponent = null; + final String setting = Settings.Secure.getString(mContentResolver, + Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT); + if (setting == null) return; + try { + mComponent = ComponentName.unflattenFromString(setting); + if (mComponent == null) return; + mUid = mContext.getPackageManager() + .getApplicationInfo(mComponent.getPackageName(), 0).uid; + } catch (Exception e) { + Log.w(TAG, "Error loading controller service", e); + } + if (DEBUG_VOL) Log.d(TAG, "Reloaded controller service: " + this); + } + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 02cacd9..ac3cd1a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -50,6 +50,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioAttributes; import android.media.AudioManager; +import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IRingtonePlayer; import android.net.Uri; @@ -179,6 +180,7 @@ public class NotificationManagerService extends SystemService { private IActivityManager mAm; AudioManager mAudioManager; + AudioManagerInternal mAudioManagerInternal; StatusBarManagerInternal mStatusBar; Vibrator mVibrator; @@ -996,6 +998,7 @@ public class NotificationManagerService extends SystemService { // Grab our optional AudioService mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); + mAudioManagerInternal = getLocalService(AudioManagerInternal.class); mZenModeHelper.onSystemReady(); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to @@ -1468,7 +1471,7 @@ public class NotificationManagerService extends SystemService { @Override public ZenModeConfig getZenModeConfig() { - enforceSystemOrSystemUI("INotificationManager.getZenModeConfig"); + enforceSystemOrSystemUIOrVolume("INotificationManager.getZenModeConfig"); return mZenModeHelper.getConfig(); } @@ -1479,6 +1482,17 @@ public class NotificationManagerService extends SystemService { } @Override + public void setZenMode(int mode) throws RemoteException { + enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode"); + final long identity = Binder.clearCallingIdentity(); + try { + mZenModeHelper.setZenMode(mode, "NotificationManager"); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void notifyConditions(String pkg, IConditionProvider provider, Condition[] conditions) { final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); @@ -1493,13 +1507,13 @@ public class NotificationManagerService extends SystemService { @Override public void requestZenModeConditions(IConditionListener callback, int relevance) { - enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions"); + enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions"); mConditionProviders.requestZenModeConditions(callback, relevance); } @Override public void setZenModeCondition(Condition condition) { - enforceSystemOrSystemUI("INotificationManager.setZenModeCondition"); + enforceSystemOrSystemUIOrVolume("INotificationManager.setZenModeCondition"); final long identity = Binder.clearCallingIdentity(); try { mConditionProviders.setZenModeCondition(condition, "binderCall"); @@ -1520,6 +1534,16 @@ public class NotificationManagerService extends SystemService { return mConditionProviders.getAutomaticZenModeConditions(); } + private void enforceSystemOrSystemUIOrVolume(String message) { + if (mAudioManagerInternal != null) { + final int vcuid = mAudioManagerInternal.getVolumeControllerUid(); + if (vcuid > 0 && Binder.getCallingUid() == vcuid) { + return; + } + } + enforceSystemOrSystemUI(message); + } + private void enforceSystemOrSystemUI(String message) { if (isCallerSystem()) return; getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, @@ -1541,7 +1565,7 @@ public class NotificationManagerService extends SystemService { @Override public ComponentName getEffectsSuppressor() { - enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor"); + enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor"); return mEffectsSuppressor; } @@ -1558,7 +1582,7 @@ public class NotificationManagerService extends SystemService { @Override public boolean isSystemConditionProviderEnabled(String path) { - enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled"); + enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled"); return mConditionProviders.isSystemConditionProviderEnabled(path); } }; |