问题分析
报错log信息如下
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: FATAL EXCEPTION: main
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: Process: com.android.music, PID: 30214
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: java.lang.RuntimeException: Unable to create service com.android.music.MediaPlaybackService: java.lang.NullPointerException: Attempt to invoke interface method 'android.media.session.ISessionController android.media.session.ISession.getController()' on a null object reference
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.app.ActivityThread.handleCreateService(ActivityThread.java:4264)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.app.ActivityThread.access$1700(ActivityThread.java:244)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1995)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.os.Looper.loop(Looper.java:223)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7740)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'android.media.session.ISessionController android.media.session.ISession.getController()' on a null object reference
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.media.session.MediaSession.<init>(MediaSession.java:199)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.media.session.MediaSession.<init>(MediaSession.java:156)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at com.android.music.MediaPlaybackService.onCreate(MediaPlaybackService.java:45)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: at android.app.ActivityThread.handleCreateService(ActivityThread.java:4252)
09-28 13:06:08.697706 30214 30214 E AndroidRuntime: ... 8 more
从报错的log中和截图中看应该是打开蓝牙的时候会触发打开 Android Open Source Music Playback Service,从而导致的报错
<service android:name="com.android.music.MediaPlaybackService"
android:exported="true"
android:label="Android Open Source Music Playback Service">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
packages/apps/Music/src/com/android/music/MediaPlaybackService.java
mSession = new MediaSession(this, "MediaPlaybackService");
frameworks/base/media/java/android/media/session/MediaSession.java
try {
mBinder = manager.createSession(mCbStub, tag, sessionInfo);
mSessionToken = new Token(Process.myUid(), mBinder.getController()); // mBinder为空导致的报错,说明上面createSession没有成功
mController = new MediaController(context, mSessionToken);
} catch (RemoteException e) {
throw new RuntimeException("Remote error creating session.", e);
}
为什么 createSession 没有成功? 因为创建了太多的session了,导致报错,和问题中的描述一致
frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java
@Override
public ISession createSession(String packageName, ISessionCallback cb, String tag,
Bundle sessionInfo, int userId) throws RemoteException {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
enforcePackageName(packageName, uid);
int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
false /* allowAll */, true /* requireFull */, "createSession", packageName);
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
}
MediaSessionRecord session = createSessionInternal(
pid, uid, resolvedUserId, packageName, cb, tag, sessionInfo);
if (session == null) {
throw new IllegalStateException("Failed to create a new session record");
}
ISession sessionBinder = session.getSessionBinder();
if (sessionBinder == null) {
throw new IllegalStateException("Invalid session record");
}
return sessionBinder;
} catch (Exception e) {
Slog.w(TAG, "Exception in creating a new session", e); //通过这行log在sys_log中找到了关键信息
throw e;
} finally {
Binder.restoreCallingIdentity(token);
}
}
mobilelog/APLog_2021_0103_082059__1/sys_log_105__2021_0929_022307
09-28 18:45:47.365026 745 3090 W MediaSessionService: Exception in creating a new session
09-28 18:45:47.365026 745 3090 W MediaSessionService: java.lang.RuntimeException: Created too many sessions. count=100) //创建了太多的session了,导致报错,和问题中的描述一致
09-28 18:45:47.365026 745 3090 W MediaSessionService: at com.android.server.media.MediaSessionService.createSessionInternal(MediaSessionService.java:601)
09-28 18:45:47.365026 745 3090 W MediaSessionService: at com.android.server.media.MediaSessionService.access$2400(MediaSessionService.java:105)
09-28 18:45:47.365026 745 3090 W MediaSessionService: at com.android.server.media.MediaSessionService$SessionManagerImpl.createSession(MediaSessionService.java:1144)
09-28 18:45:47.365026 745 3090 W MediaSessionService: at android.media.session.ISessionManager$Stub.onTransact(ISessionManager.java:289)
09-28 18:45:47.365026 745 3090 W MediaSessionService: at android.os.Binder.execTransactInternal(Binder.java:1154)
09-28 18:45:47.365026 745 3090 W MediaSessionService: at android.os.Binder.execTransact(Binder.java:1123)
09-28 18:45:47.411609 745 791 D BluetoothManagerService: MESSAGE_BLUETOOTH_STATE_CHANGE: TURNING_ON > ON
09-28 18:45:47.412572 745 791 D BluetoothManagerService: Broadcasting onBluetoothStateChange(true) to 24 receivers.
09-28 18:45:47.419077 745 791 D BluetoothManagerService: Creating new ProfileServiceConnections object for profile: 1
然后找到下面这段代码,目前肯定是满足了第一个条件,private static final int SESSION_CREATION_LIMIT_PER_UID = 100;
if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
&& !hasMediaControlPermission(callerPid, callerUid)) {
throw new RuntimeException("Created too many sessions. count="
+ sessionCount + ")");
}
private boolean hasMediaControlPermission(int pid, int uid) {
// Check if it's system server or has MEDIA_CONTENT_CONTROL.
// Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
// check here.
if (uid == Process.SYSTEM_UID || mContext.checkPermission(
android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else if (DEBUG) {
Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
}
return false;
}
要满足第二个条件hasMediaControlPermission()
,只有sharedUserId=system
(platform签名),或者有media_content_control
权限的应用才可以,尝试了给 Music 添加media_content_control
权限无果,不知道问题出在哪里,有兴趣的朋友可以自己尝试一下
最终修改方案
1.不预置music应用
2.或给Music应用添加platform的签名
packages/apps/Music/Android.bp
android_app {
name: "Music",
srcs: ["src/**/*.java"],
sdk_version: "current",
product_specific: true,
certificate: "platform",// 添加platform签名
optimize: {
proguard_flags_files: ["proguard.flags"],
},
}
测试方法
1.修改 MediaSessionService.java 中的 SESSION_CREATION_LIMIT_PER_UID = 5; //可以减少测试次数
2.在 final int sessionCount = user.mUidToSessionCount.get(callerUid, 0); 后面添加如下log
android.util.Log.e(TAG, "Time:2021-10-14 15:44:03-->"+Thread.currentThread().getStackTrace()[2].getClassName()+"-->"+Thread.currentThread().getStackTrace()[2].getMethodName()+"()-->"+Thread.currentThread().getStackTrace()[2].getLineNumber()+" sessionCount:"+sessionCount+" hasMediaControlPermission:"+hasMediaControlPermission);
3.运行自己写的脚本 runTest bt
echo "run BT test begin!"
for (( i = 0; i < 10000; i++ )); do
adb shell svc bluetooth enable
echo "enable bluetooth for $i times!"
sleep 2
adb shell svc bluetooth disable
echo "disable bluetooth for $i times!"
sleep 2
done
echo "run BT test end!"
有几个疑问
1.为什么打开关闭蓝牙的时候没有关闭session?如何关闭session?
2.为什么google的Music不会有这个问题?
3.SESSION_CREATION_LIMIT_PER_UID = 100; 这个值是否可以改大?
4.有权限之后,session可以无限创建吗?会不会OOM或者且他问题?
欢迎大家一起学习讨论~~