Android 深入理解 ANR 触发原理:ContentProvider

 

四 、ContentProvider Timeout

4.1 attachApplicationLocked

 private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

        // Find the application record that is being attached...  either via
        // the pid if we are running in multiple processes, or just pull the
        // next app record if we are emulating process with anonymous threads.
        ProcessRecord app;
        long startTime = SystemClock.uptimeMillis();
        long bindApplicationTimeMillis;
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
            if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
                String processName = null;
                final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
                if (pending != null) {
                    processName = pending.processName;
                }
                final String msg = "attachApplicationLocked process:" + processName
                        + " startSeq:" + startSeq
                        + " pid:" + pid
                        + " belongs to another existing app:" + app.processName
                        + " startSeq:" + app.startSeq;
                Slog.wtf(TAG, msg);
                // SafetyNet logging for b/131105245.
                EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
                // If there is already an app occupying that pid that hasn't been cleaned up
                cleanUpApplicationRecordLocked(app, false, false, -1,
                            true /*replacingPid*/);
                mPidsSelfLocked.remove(app);
                app = null;
            }
        } else {
            app = null;
        }

        // It's possible that process called attachApplication before we got a chance to
        // update the internal state.
        if (app == null && startSeq > 0) {
            final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
            if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
                    && mProcessList.handleProcessStartedLocked(pending, pid, pending
                            .isUsingWrapper(),
                            startSeq, true)) {
                app = pending;
            }
        }

        if (app == null) {
            Slog.w(TAG, "No pending application record for pid " + pid
                    + " (IApplicationThread " + thread + "); dropping process");
            EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
            if (pid > 0 && pid != MY_PID) {
                killProcessQuiet(pid);
                //TODO: killProcessGroup(app.info.uid, pid);
            } else {
                try {
                    thread.scheduleExit();
                } catch (Exception e) {
                    // Ignore exceptions.
                }
            }
            return false;
        }

        // If this application record is still attached to a previous
        // process, clean it up now.
        if (app.thread != null) {
            handleAppDiedLocked(app, true, true);
        }

        // Tell the process all about itself.

        if (DEBUG_ALL) Slog.v(
                TAG, "Binding process pid " + pid + " to record " + app);

        final String processName = app.processName;
        try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            mProcessList.startProcessLocked(app,
                    new HostingRecord("link fail", processName));
            return false;
        }

        EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

        app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
        app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
        app.forcingToImportant = null;
        updateProcessForegroundLocked(app, false, 0, false);
        app.hasShownUi = false;
        app.setDebugging(false);
        app.cached = false;
        app.killedByAm = false;
        app.killed = false;


        // We carefully use the same state that PackageManager uses for
        // filtering, since we use this flag to decide if we need to install
        // providers when user is unlocked later
        app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);

        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

        if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
            Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
            msg.obj = app;
            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
        }

        checkTime(startTime, "attachApplicationLocked: before bindApplication");

        if (!normalMode) {
            Slog.i(TAG, "Launching preboot mode app: " + app);
        }
......
}

上面代码快看两个关键信息:Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG); 这个message 是在10s后执行。

 EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);会再event log中生成,关键字:am_proc_bound.

4.2 publishContentProviders

public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
        if (providers == null) {
            return;
        }

        enforceNotIsolatedCaller("publishContentProviders");
        synchronized (this) {
            final ProcessRecord r = getRecordForAppLocked(caller);//获取进程

            /// M: Fix NullPointerException @{
            if (DEBUG_MU && r != null) {
                Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
            }
            /// M: Fix NullPointerException @}

            if (r == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                      + " (pid=" + Binder.getCallingPid()
                      + ") when publishing content providers");
            }

            final long origId = Binder.clearCallingIdentity();


     //providers是系统在开机启动的时候,会对所有包含contentprovide的扫描收集的集合。
            final int N = providers.size();
            for (int i = 0; i < N; i++) {
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    continue;
                }
                ContentProviderRecord dst = r.pubProviders.get(src.info.name);

                /// M: Fix NullPointerException @{
                if (DEBUG_MU && dst != null) {
                    Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                }
                /// M: Fix NullPointerException @}

                if (dst != null) {//当找到对应的contentprovide的时候
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    mProviderMap.putProviderByClass(comp, dst);
                    String names[] = dst.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        mProviderMap.putProviderByName(names[j], dst);
                    }

                    int launchingCount = mLaunchingProviders.size();//保存已经启动的provider集合。
                    int j;
                    boolean wasInLaunchingProviders = false;
					//如果已经启动provide 等于我们所要求的。wasInLaunchingProviders=true。移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG 
                    for (j = 0; j < launchingCount; j++) {
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true;
                            j--;
                            launchingCount--;
                        }
                    }
                    if (wasInLaunchingProviders) {
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
                    // Make sure the package is associated with the process.
                    // XXX We shouldn't need to do this, since we have added the package
                    // when we generated the providers in generateApplicationProvidersLocked().
                    // But for some reason in some cases we get here with the package no longer
                    // added...  for now just patch it in to make things happy.
                    r.addPackage(dst.info.applicationInfo.packageName,
                            dst.info.applicationInfo.longVersionCode, mProcessStats);
                    synchronized (dst) {
                        dst.provider = src.provider;
                        dst.setProcess(r);
                        dst.notifyAll();
                    }
                    updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
                    maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                            src.info.authority);
                }
            }

            Binder.restoreCallingIdentity(origId);
        }
    }

在这个函数中我们移除  mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); 

 

4.3 CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG

  case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
                ProcessRecord app = (ProcessRecord)msg.obj;
                synchronized (ActivityManagerService.this) {
                    processContentProviderPublishTimedOutLocked(app);
                }
            } break;
 private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
        cleanupAppInLaunchingProvidersLocked(app, true);
        mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
    }
boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
    boolean restart = false;
    for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
        ContentProviderRecord cpr = mLaunchingProviders.get(i);
        if (cpr.launchingApp == app) {
            if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
                restart = true;
            } else {
                //移除死亡的provider
                removeDyingProviderLocked(app, cpr, true);
            }
        }
    }
    return restart;
}
private final boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr, boolean always) {
  final boolean inLaunching = mLaunchingProviders.contains(cpr);

  if (!inLaunching || always) {
      synchronized (cpr) {
          cpr.launchingApp = null;
          cpr.notifyAll(); //通知处于等待状态的进程
      }
      mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
      String names[] = cpr.info.authority.split(";");
      for (int j = 0; j < names.length; j++) {
          mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
      }
  }

  for (int i = cpr.connections.size() - 1; i >= 0; i--) {
      ContentProviderConnection conn = cpr.connections.get(i);
      if (conn.waiting) {
          if (inLaunching && !always) {
              continue;
          }
      }
      ProcessRecord capp = conn.client;
      conn.dead = true;
      if (conn.stableCount > 0) {
          if (!capp.persistent && capp.thread != null
                  && capp.pid != 0
                  && capp.pid != MY_PID) {
              //非persistent进程,则杀掉跟provider具有依赖关系的进程
              capp.kill("depends on provider "
                      + cpr.name.flattenToShortString()
                      + " in dying proc " + (proc != null ? proc.processName : "??"), true);
          }
      } else if (capp.thread != null && conn.provider.provider != null) {
          //unstable的类型则不会被杀,也会调用到
          capp.thread.unstableProviderDied(conn.provider.provider.asBinder());

          cpr.connections.remove(i);
          if (conn.client.conProviders.remove(conn)) {
              stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
          }
      }
  }

  if (inLaunching && always) {
      mLaunchingProviders.remove(cpr);
  }
  return inLaunching;

removeDyingProviderLocked()方法的功能非常值得注意,引用计数跟进程的存活息息相关:

  • 对于stable类型的provider(即conn.stableCount > 0),则会杀掉所有跟该provider建立stable连接的非persistent进程.
  • 对于unstable类的provider(即conn.unstableCount > 0),并不会导致client进程被级联所杀.
boolean removeProcessLocked(ProcessRecord app,
            boolean callerWillRestart, boolean allowRestart, String reason) {
        final String name = app.processName;
        final int uid = app.uid;
        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
                "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");

        ProcessRecord old = mProcessNames.get(name, uid);
        if (old != app) {
            // This process is no longer active, so nothing to do.
            Slog.w(TAG, "Ignoring remove of inactive process: " + app);
            return false;
        }
        removeProcessNameLocked(name, uid);
        mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());

        boolean needRestart = false;
        if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
                .pendingStart)) {
            int pid = app.pid;
            if (pid > 0) {
                mService.mPidsSelfLocked.remove(app);//移除mPidsSelfLocked的进程信息。
                mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);//removeMessages PROC_START_TIMEOUT_MSG,每个进程创建都会发生一个timeout 广播 :PROC_START_TIMEOUT_MSG
                mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                if (app.isolated) {
                    mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
                    mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
                }
            }
            boolean willRestart = false;
			//如果是persistent app 就重启。
            if (app.isPersistent() && !app.isolated) {
                if (!callerWillRestart) {
                    willRestart = true;
                } else {
                    needRestart = true;
                }
            }
            app.kill(reason, true);//杀死进程,输出event log 。
            mService.handleAppDiedLocked(app, willRestart, allowRestart);
            if (willRestart) {
                removeLruProcessLocked(app);
                mService.addAppLocked(app.info, null, false, null /* ABI override */);
            }
        } else {
            mRemovedProcesses.add(app);
        }

        return needRestart;
    }
 void kill(String reason, boolean noisy) {
        if (!killedByAm) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
            if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
                mService.reportUidInfoMessageLocked(TAG,
                        "Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
                        info.uid);
            }
            if (pid > 0) {
			//在event log中会搜索到am_kill 关键字。
                EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
                Process.killProcessQuiet(pid);
                ProcessList.killProcessGroup(uid, pid);
            } else {
                pendingStart = false;
            }
            if (!mPersistent) {
                killed = true;
                killedByAm = true;
            }
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

总结:contentprovide timeout 不会产生ANR,跟之前的servcie ,broadcast 不相同。而是会杀死相关进程。

而关键的log是:1.EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);

                           2.  EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

                       

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值