Merge "Multi-user ringtone playback." into jb-mr1-dev

This commit is contained in:
Jeff Sharkey
2012-09-26 13:38:21 -07:00
committed by Android (Google) Code Review
9 changed files with 95 additions and 11 deletions

View File

@@ -124,6 +124,9 @@ public class NotificationManager
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
@@ -143,6 +146,9 @@ public class NotificationManager
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,

View File

@@ -16,10 +16,13 @@
package android.net;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Environment.UserEnvironment;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charsets;
@@ -2288,4 +2291,39 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
builder = builder.appendEncodedPath(pathSegment);
return builder.build();
}
/**
* If this {@link Uri} is {@code file://}, then resolve and return its
* canonical path. Also fixes legacy emulated storage paths so they are
* usable across user boundaries. Should always be called from the app
* process before sending elsewhere.
*
* @hide
*/
public Uri getCanonicalUri() {
if ("file".equals(getScheme())) {
final String canonicalPath;
try {
canonicalPath = new File(getPath()).getCanonicalPath();
} catch (IOException e) {
return this;
}
if (Environment.isExternalStorageEmulated()) {
final String legacyPath = Environment.getLegacyExternalStorageDirectory()
.toString();
// Splice in user-specific path when legacy path is found
if (canonicalPath.startsWith(legacyPath)) {
return Uri.fromFile(new File(
Environment.getExternalStorageDirectory().toString(),
canonicalPath.substring(legacyPath.length() + 1)));
}
}
return Uri.fromFile(new File(canonicalPath));
} else {
return this;
}
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2012, 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 android.os;
parcelable UserHandle;

View File

@@ -2166,6 +2166,8 @@ public class RemoteViews implements Parcelable, Filter {
* @param value The value to pass to the method.
*/
public void setUri(int viewId, String methodName, Uri value) {
// Resolve any filesystem path before sending remotely
value = value.getCanonicalUri();
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
}

View File

@@ -17,6 +17,7 @@
package android.media;
import android.net.Uri;
import android.os.UserHandle;
/**
* @hide
@@ -28,6 +29,6 @@ interface IRingtonePlayer {
boolean isPlaying(IBinder token);
/** Used for Notification sound playback. */
void playAsync(in Uri uri, boolean looping, int streamType);
void playAsync(in Uri uri, in UserHandle user, boolean looping, int streamType);
void stopAsync();
}

View File

@@ -225,8 +225,9 @@ public class Ringtone {
mLocalPlayer.start();
}
} else if (mAllowRemote) {
final Uri canonicalUri = mUri.getCanonicalUri();
try {
mRemotePlayer.play(mRemoteToken, mUri, mStreamType);
mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType);
} catch (RemoteException e) {
Log.w(TAG, "Problem playing ringtone: " + e);
}

View File

@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />

View File

@@ -17,6 +17,7 @@
package com.android.systemui.media;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.IAudioService;
import android.media.IRingtonePlayer;
import android.media.Ringtone;
@@ -26,6 +27,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
import com.android.systemui.SystemUI;
@@ -70,9 +72,10 @@ public class RingtonePlayer extends SystemUI {
private final IBinder mToken;
private final Ringtone mRingtone;
public Client(IBinder token, Uri uri, int streamType) {
public Client(IBinder token, Uri uri, UserHandle user, int streamType) {
mToken = token;
mRingtone = new Ringtone(mContext, false);
mRingtone = new Ringtone(getContextForUser(user), false);
mRingtone.setStreamType(streamType);
mRingtone.setUri(uri);
}
@@ -90,12 +93,16 @@ public class RingtonePlayer extends SystemUI {
private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
@Override
public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")");
if (LOGD) {
Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Binder.getCallingUid() + ")");
}
Client client;
synchronized (mClients) {
client = mClients.get(token);
if (client == null) {
client = new Client(token, uri, streamType);
final UserHandle user = Binder.getCallingUserHandle();
client = new Client(token, uri, user, streamType);
token.linkToDeath(client, 0);
mClients.put(token, client);
}
@@ -131,12 +138,13 @@ public class RingtonePlayer extends SystemUI {
}
@Override
public void playAsync(Uri uri, boolean looping, int streamType) {
if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")");
public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) {
if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
mAsyncPlayer.play(mContext, uri, looping, streamType);
mAsyncPlayer.play(getContextForUser(user), uri, looping, streamType);
}
@Override
@@ -149,6 +157,14 @@ public class RingtonePlayer extends SystemUI {
}
};
private Context getContextForUser(UserHandle user) {
try {
return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Clients:");

View File

@@ -890,6 +890,7 @@ public class NotificationManagerService extends INotificationManager.Stub
userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, userId, true, true, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
// Limit the number of notifications that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
@@ -991,7 +992,6 @@ public class NotificationManagerService extends INotificationManager.Stub
}
if (notification.icon != 0) {
final UserHandle user = new UserHandle(userId);
final StatusBarNotification n = new StatusBarNotification(
pkg, id, tag, r.uid, r.initialPid, score, notification, user);
if (old != null && old.statusBarKey != null) {
@@ -1063,7 +1063,7 @@ public class NotificationManagerService extends INotificationManager.Stub
try {
final IRingtonePlayer player = mAudioService.getRingtonePlayer();
if (player != null) {
player.playAsync(uri, looping, audioStreamType);
player.playAsync(uri, user, looping, audioStreamType);
}
} catch (RemoteException e) {
} finally {