以下是源码分析流程:
第一步:
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
1.
public void onCreate() {
super.onCreate();
Log.v(TAG, "SystemUIApplication created.");
// This line is used to setup Dagger's dependency injection and should be kept at the
// top of this method.
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin("DependencyInjection");
mContextAvailableCallback.onContextAvailable(this);
mRootComponent = SystemUIFactory.getInstance().getRootComponent();
mComponentHelper = mRootComponent.getContextComponentHelper();
mBootCompleteCache = mRootComponent.provideBootCacheImpl();
log.traceEnd();
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
// the theme set there.
setTheme(R.style.Theme_SystemUI);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mBootCompleteCache.isBootComplete()) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
mBootCompleteCache.setBootComplete();
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted();
}
}
}
}, bootCompletedFilter);
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
if (!mBootCompleteCache.isBootComplete()) return;
// Update names of SystemUi notification channels
NotificationChannels.createAll(context);
}
}
}, localeChangedFilter);
} else {
// We don't need to startServices for sub-process that is doing some tasks.
// (screenshots, sweetsweetdesserts or tuner ..)
String processName = ActivityThread.currentProcessName();
ApplicationInfo info = getApplicationInfo();
if (processName != null && processName.startsWith(info.processName + ":")) {
return;
}
// For a secondary user, boot-completed will never be called because it has already
// been broadcasted on startup for the primary SystemUI process. Instead, for
// components which require the SystemUI component to be initialized per-user, we
// start those components now for the current non-system user.
startSecondaryUserServicesIfNeeded(); 会走这里
}
}
2.
void startSecondaryUserServicesIfNeeded() {
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponentsPerUser( //这里从config.xml文件里面获取服务名称
getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names);
}
3.
frameworks/base/packages/SystemUI/res/values/config.xml
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.AuthController</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.accessibility.SystemActions</item>
<item>com.android.systemui.toast.ToastUI</item>
</string-array>
4.
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleteCache.setBootComplete();
if (DEBUG) {
Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
}
final DumpManager dumpManager = mRootComponent.createDumpManager();
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin(metricsPrefix);
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InstantiationException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start(); 这里会启动RingtonePlayer 服务
log.traceEnd();
// Warn if initialization of component takes too long
ti = System.currentTimeMillis() - ti;
if (ti > 1000) {
Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
}
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
mRootComponent.getInitController().executePostInitTasks();
log.traceEnd();
mServicesStarted = true;
}
第二步:
frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
public void start() {
mAsyncPlayer.setUsesWakeLock(mContext);
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
try {
mAudioService.setRingtonePlayer(mCallback); mAudioService设置IRingtonePlayer的回调
} catch (RemoteException e) {
Log.e(TAG, "Problem registering RingtonePlayer: " + e);
}
}
@Override
public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
if (UserHandle.ALL.equals(user)) {
user = UserHandle.SYSTEM;
}
Log.e(TAG, Log.getStackTraceString(new Throwable()));
mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
}
第三步:
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
if (mAssistants.isEnabled()) {
mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
mHandler.post(new PostNotificationRunnable(r.getKey())); 这里开始PostNotificationRunnable 线程运行
}
final boolean isPackageSuspended =
isPackagePausedOrSuspended(r.getSbn().getPackageName(), r.getUid());
r.setHidden(isPackageSuspended);
int buzzBeepBlinkLoggingCode = 0;
if (!r.isHidden()) {
buzzBeepBlinkLoggingCode = buzzBeepBlinkLocked(r);
}
int buzzBeepBlinkLocked(NotificationRecord record) {
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.0");
if (mIsAutomotive && !mNotificationEffectsEnabledForAutomotive) {
return 0;
}
boolean buzz = false;
boolean beep = false;
boolean blink = false;
final Notification notification = record.getSbn().getNotification();
final String key = record.getKey();
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.0.0");
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold =
mIsAutomotive
? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
: record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
// Remember if this notification already owns the notification channels.
boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
// These are set inside the conditional if the notification is allowed to make noise.
boolean hasValidVibrate = false;
boolean hasValidSound = false;
boolean sentAccessibilityEvent = false;
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.1");
// If the notification will appear in the status bar, it should send an accessibility event
final boolean suppressedByDnd = record.isIntercepted()
&& (record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
if (!record.isUpdate
&& record.getImportance() > IMPORTANCE_MIN
&& !suppressedByDnd) {
sendAccessibilityEvent(notification, record.getSbn().getPackageName());
sentAccessibilityEvent = true;
}
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.2 aboveThreshold="+aboveThreshold);
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.2 isNotificationForCurrentUser(record)="+ isNotificationForCurrentUser(record));
if (aboveThreshold && isNotificationForCurrentUser(record)) {
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.2 mSystemReady="+mSystemReady);
if(mAudioManager != null){
Log.e(TAG,"zhanghui buzzBeepBlinkLocked mAudioManager is true");
}
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
long[] vibration = record.getVibration();
// Demote sound to vibration if vibration missing & phone in vibration mode.
if (vibration == null
&& hasValidSound
&& (mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_VIBRATE)
&& mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) {
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
Log.e(TAG,"zhanghui hasAudibleAlert="+hasAudibleAlert);
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) { //shouldMuteNotificationLocked这里很关键,高通原生第一次升级默认没有提示音(比如:蓝牙传输接收文件没有提示音)
if (!sentAccessibilityEvent) {
sendAccessibilityEvent(notification, record.getSbn().getPackageName());
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
Log.e(TAG,"zhanghui buzzBeepBlinkLocked 1.2 hasValidSound="+hasValidSound);
if (hasValidSound) {
if (isInCall()) {
playInCallNotification();
beep = true;
} else {
beep = playSound(record, soundUri); 跑这里调用
}
if(beep) {
mSoundNotificationKey = key;
}
}
final boolean ringerModeSilent =
mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_SILENT;
if (!isInCall() && hasValidVibrate && !ringerModeSilent) {
buzz = playVibration(record, vibration, hasValidSound);
if(buzz) {
mVibrateNotificationKey = key;
}
}
} else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
hasValidSound = false;
}
}
}
// If a notification is updated to remove the actively playing sound or vibrate,
// cancel that feedback now
if (wasBeep && !hasValidSound) {
clearSoundLocked();
}
if (wasBuzz && !hasValidVibrate) {
clearVibrateLocked();
}
// light
// release the light
boolean wasShowLights = mLights.remove(key);
if (canShowLightsLocked(record, aboveThreshold)) {
mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight && mAttentionLight != null) {
mAttentionLight.pulse();
}
blink = true;
} else if (wasShowLights) {
updateLightsLocked();
}
final int buzzBeepBlink = (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
if (buzzBeepBlink > 0) {
// Ignore summary updates because we don't display most of the information.
if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
}
} else if (record.canBubble()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: bubble");
}
} else {
record.setInterruptive(true);
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is interruptive: alerted");
}
}
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
.setSubtype(buzzBeepBlink));
EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
}
record.setAudiblyAlerted(buzz || beep);
return buzzBeepBlink;
}
private boolean playSound(final NotificationRecord record, Uri soundUri) {
boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
// play notifications if there is no user of exclusive audio focus
// and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
// VIBRATE ringer mode)
if (!mAudioManager.isAudioFocusExclusive()
&& (mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
final long identity = Binder.clearCallingIdentity();
try {
final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
if (player != null) {
if (true) Slog.v(TAG, "zhanghui Playing sound " + soundUri
+ " with attributes " + record.getAudioAttributes());
Log.e(TAG, Log.getStackTraceString(new Throwable()));
player.playAsync(soundUri, record.getSbn().getUser(), looping, 这里跑到第二步的RingtonePlayer回调函数的playAsync,这里传入的soundUri 就是后面播放的提示音文件缓存
record.getAudioAttributes());
return true;
}
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
}
return false;
}
第四步:
frameworks/base/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
public void start() {
mAsyncPlayer.setUsesWakeLock(mContext);
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
try {
mAudioService.setRingtonePlayer(mCallback); mAudioService设置IRingtonePlayer的回调
} catch (RemoteException e) {
Log.e(TAG, "Problem registering RingtonePlayer: " + e);
}
}
@Override
public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
}
if (UserHandle.ALL.equals(user)) {
user = UserHandle.SYSTEM;
}
Log.e(TAG, Log.getStackTraceString(new Throwable()));
mAsyncPlayer.play(getContextForUser(user), uri, looping, aa); 这里跑到NotificationPlayer类的play()
}
frameworks/base/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
cmd.code = PLAY;
cmd.context = context;
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = attributes;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
}
}
private void enqueueLocked(Command cmd) {
mCmdQueue.add(cmd);
if (mThread == null) {
acquireWakeLock();
mThread = new CmdThread();
mThread.start();
}
}
private final class CmdThread extends java.lang.Thread {
CmdThread() {
super("NotificationPlayer-" + mTag);
}
public void run() {
while (true) {
Command cmd = null;
synchronized (mCmdQueue) {
if (DEBUG) Log.d(mTag, "RemoveFirst");
cmd = mCmdQueue.removeFirst();
}
switch (cmd.code) {
case PLAY:
if (DEBUG) Log.d(mTag, "PLAY");
startSound(cmd); 这里跑到CreationAndCompletionThread 线程
break;
case STOP:
if (DEBUG) Log.d(mTag, "STOP");
final MediaPlayer mp;
synchronized (mPlayerLock) {
mp = mPlayer;
mPlayer = null;
}
if (mp != null) {
long delay = SystemClock.uptimeMillis() - cmd.requestTime;
if (delay > 1000) {
Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
}
try {
mp.stop();
} catch (Exception e) { }
mp.release();
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
mAudioManagerWithAudioFocus.abandonAudioFocus(null);
mAudioManagerWithAudioFocus = null;
}
}
synchronized (mCompletionHandlingLock) {
if ((mLooper != null) &&
(mLooper.getThread().getState() != Thread.State.TERMINATED))
{
if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
mLooper.quit();
}
}
} else {
Log.w(mTag, "STOP command without a player");
}
break;
}
private final class CreationAndCompletionThread extends Thread {
public Command mCmd;
public CreationAndCompletionThread(Command cmd) {
super();
mCmd = cmd;
}
public void run() {
Looper.prepare();
// ok to modify mLooper as here we are
// synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
MediaPlayer player = null;
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
try {
player = new MediaPlayer();
if (mCmd.attributes == null) {
mCmd.attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
}
player.setAudioAttributes(mCmd.attributes);
player.setDataSource(mCmd.context, mCmd.uri); 这里调用了MediaPlayer里面的setDataSource
player.setLooping(mCmd.looping);
player.setOnCompletionListener(NotificationPlayer.this);
player.setOnErrorListener(NotificationPlayer.this);
player.prepare();
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (!audioManager.isMusicActiveRemotely()) {
synchronized (mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus == null) {
if (DEBUG) Log.d(mTag, "requesting AudioFocus");
int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
if (mCmd.looping) {
focusGain = AudioManager.AUDIOFOCUS_GAIN;
}
mNotificationRampTimeMs = audioManager.getFocusRampTimeMs(
focusGain, mCmd.attributes);
audioManager.requestAudioFocus(null, mCmd.attributes,
focusGain, 0);
mAudioManagerWithAudioFocus = audioManager;
} else {
if (DEBUG) Log.d(mTag, "AudioFocus was previously requested");
}
}
}
}
// FIXME Having to start a new thread so we can receive completion callbacks
// is wrong, as we kill this thread whenever a new sound is to be played. This
// can lead to AudioFocus being released too early, before the second sound is
// done playing. This class should be modified to use a single thread, on which
// command are issued, and on which it receives the completion callbacks.
if (DEBUG) { Log.d(mTag, "notification will be delayed by "
+ mNotificationRampTimeMs + "ms"); }
try {
Thread.sleep(mNotificationRampTimeMs);
} catch (InterruptedException e) {
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
player.start();
if (DEBUG) { Log.d(mTag, "player.start"); }
} catch (Exception e) {
if (player != null) {
player.release();
player = null;
}
Log.w(mTag, "error loading sound for " + mCmd.uri, e);
// playing the notification didn't work, revert the focus request
abandonAudioFocusAfterError();
}
final MediaPlayer mp;
synchronized (mPlayerLock) {
mp = mPlayer;
mPlayer = player;
}
if (mp != null) {
if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
mp.release();
}
this.notify();
}
Looper.loop();
}
};
frameworks/base/media/java/android/media/MediaPlayer.java
private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) { 这里跑到了ContentResolver里面的openAssetFileDescriptor
setDataSource(afd);
return true;
} catch (NullPointerException | SecurityException | IOException ex) {
return false;
}
}
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
public void setDataSource(@NonNull Context context, @NonNull Uri uri)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(context, uri, null, null);
}
private void setDataSource(String path, String[] keys, String[] values,
List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
Log.e(TAG,"zhanghui path="+path);
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
}
final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
Log.e(TAG,"zhanghui is.getFD()="+is.getFD());
setDataSource(is.getFD());
}
}
frameworks/base/core/java/android/content/ContentResolver.java
public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
@NonNull String mode) throws FileNotFoundException {
return openAssetFileDescriptor(uri, mode, null);
}
public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
@NonNull String mode, @Nullable CancellationSignal cancellationSignal)
throws FileNotFoundException {
Objects.requireNonNull(uri, "uri");
Objects.requireNonNull(mode, "mode");
try {
if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, cancellationSignal);
} catch (RemoteException e) {
return null;
}
String scheme = uri.getScheme();
if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
if (!"r".equals(mode)) {
throw new FileNotFoundException("Can't write resources: " + uri);
}
OpenResourceIdResult r = getResourceId(uri);
try {
return r.r.openRawResourceFd(r.id);
} catch (Resources.NotFoundException ex) {
throw new FileNotFoundException("Resource does not exist: " + uri);
}
} else if (SCHEME_FILE.equals(scheme)) {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
} else {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try {
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
fd = unstableProvider.openAssetFile(
mPackageName, mAttributionTag, uri, mode,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openAssetFile(
mPackageName, mAttributionTag, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
}
if (stableProvider == null) {
stableProvider = acquireProvider(uri);
}
releaseUnstableProvider(unstableProvider);
unstableProvider = null;
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
} catch (RemoteException e) {
// Whatever, whatever, we'll go away.
throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
}
}
}
}