首页
工具
隐私协议
App Privacy Policy
更多
作品
关于我们
Search
1
android5遇到INSTALL_FAILED_DEXOPT 解决办法
1,666 阅读
2
设置max_connections无效
1,484 阅读
3
FlexboxLayout+recyclerView实现自动换行
1,379 阅读
4
Nginx配置多个域名
1,257 阅读
5
Android P http网络请求失败
1,231 阅读
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
登录
Search
标签搜索
android
kotlin
webrtc
kurento
mysql
adb
nginx
flutter
rsa
微信
git
Yii2
md5
加密
dart
aes
wechat
windows
小程序
dexopt
Typecho
累计撰写
80
篇文章
累计收到
3
条评论
首页
栏目
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
页面
工具
隐私协议
App Privacy Policy
作品
关于我们
搜索到
52
篇与
android
的结果
2024-08-13
adb查看当前包名
windows:adb shell dumpsys window w |findstr \/ |findstr name=mac端:adb shell dumpsys window w |grep / |grep name=
2024年08月13日
15 阅读
0 评论
0 点赞
2023-01-13
linux命令dd
dd命令的作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。可以用于测试磁盘命令、数据备份或恢复等dd 命令格式如下:dd [bs=<字节数>][cbs=<字节数>][conv=<关键字>][count=<区块数>][ibs=<字节数>][if=<文件>][obs=<字节数>][of=<文件>][seek=<区块数>][skip=<区块数>][--help][--version]dd if=path/to/input_file of=/path/to/output_file bs=block_size count=number_of_blocksif=file 输入文件名,缺省为标准输入。 从file读取,如if=/dev/zero,该设备无穷尽地提供0,(不产生读磁盘IO)of=file 输出文件名,缺省为标准输出。 向file写出,可以写文件,可以写裸设备。如of=/dev/null,"黑洞",它等价于一个只写文件. 所有写入它的内容都会永远丢失. (不产生写磁盘IO)ibs=bytes 一次读入 bytes 个字节(即一个块大小为 bytes 个字节)。obs=bytes 一次写 bytes 个字节(即一个块大小为 bytes 个字节)。bs=bytes 同时设置读写块的大小为 bytes ,可代替 ibs 和 obs。如bs=8k 每次读或写的大小,即一个块的大小为8K。cbs=bytes 一次转换 bytes 个字节,即转换缓冲区大小。skip=blocks 从输入文件开头跳过 blocks 个块后再开始复制。seek=blocks 从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)。count=blocks 仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。iflag=FLAGS 指定读的方式FLAGS,参见“FLAGS参数说明”oflag=FLAGS 指定写的方式FLAGS,参见“FLAGS参数说明”测试方式:使用dd指令,对磁盘进行连续写入,不使用内存缓冲区,每次写入8k的数据,总共写入20万次,产生1.6G大小的文件。测试指令:dd if=/dev/zero of=/data01/test.dbf bs=8k count=200k conv=fdatasyncdd用于复制,从if读出,写到of;if=/dev/zero(产生字符)不产生IO,因此可以用来测试纯写速度;bs是每次读或写的大小,即一个块的大小,count是读写块的数量;conv=fdatasync表示只把文件的“数据”写入磁盘会在/data01下生成一个文件test.dbf,count * bs 等于最终大小,记得删除。从源 /dev/zero 读取写入到驱动盘的时候(测试写),当从驱动盘读取时写入到/dev/null(测试读)。在整个操作过程中, DD 命令会跟踪数据传输的速度并且报告出结果。/dev/null和/dev/zero的区别/dev/null,它是空设备,也称为位桶(bit bucket)、回收站、无底洞,可以向它输出任何数据。任何写入它的输出都会被抛弃。如果不想让消息以标准输出显示或写入文件,那么可以将消息重定向到位桶。/dev/zero,是一个输入设备,可用它来初始化文件。该设备无穷尽地提供0,可以使用任何需要的数目——设备提供的要多的多。他可以用于向设备或文件写入字符串0。使用sync、fsync、fdatasync、dsyncconv=fsync,表示把文件的“数据”和“metadata”都写入磁盘(metadata包括size、访问时间st_atime & st_mtime等等),因为文件的数据和metadata通常存在硬盘的不同地方,因此fsync至少需要两次IO写操作,fsync 与fdatasync相差不大。conv=fdatasync,dd命令执行到最后会真正执行一次“同步(sync)”操作,,这样算出来的时间才是比较符合实际使用结果的。conv=fdatasync表示只把文件的“数据”写入磁盘,fsync 与fdatasync相差不大。oflag=dsync,dd在执行时每次都会进行同步写入操作。每次读取8k后就要先把这8k写入磁盘,然后再读取下面一个8k,一共重复4K次,生成一个32M文件。这是最慢的一种方式了,基本上没有用到写缓存(write cache)。也是比较准确的。conv=fdatasync与oflag=dsync的区别在于:sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。所以看到的fdatasync速度比dsync好。如果不加任何参数,dd默认的方式不包括“同步(sync)”命令(没加关于操作系统“写缓存”的参数,默认“写缓存”启作用),也就是说,dd命令完成前并没有让系统真正把文件写到磁盘上。dd先把数据写到操作系统“写缓存”,就完成了写操作。所以以上命令只是单纯地把数据读到内存缓冲当中(写缓存[write cache])。通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数,把“写缓存”中的数据刷入磁盘。
2023年01月13日
579 阅读
0 评论
0 点赞
2023-01-05
Android之ActivityThread
每一个App在启动时,都会由Zygote fork出一个进程,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。当进程创建后就要真正去启动一个App了,会调用ActivityThread的main方法。 frameworks/base/core/java/android/app/ActivityThread.java ActivityThread分析:main方法public static void main(String[] args) { ...... //1.创建主线程looper,主线程初始化 Looper.prepareMainLooper(); //2.主线程Handler初始化 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); }主线程初始化frameworks/base/core/java/android/os/Looper.java//主线程Looper的初始化 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //普通线程初始化 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static @Nullable Looper myLooper() { return sThreadLocal.get(); }从上面的代码中可以看出,主线程初始化时,prepare传入false,而普通线程初始化时传入true,这个参数为quitAllowed,表示线程是否可以退出,主线程无法退出。prepare函数中创建一个Looper对象,并将对象保存在ThreadLocal中。在prepare之后,又将主线程Looper赋值给了成员变量sMainLooper,这个成员变量的作用是向其他线程提供主线程Looper对象。public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }这样当我们调用Looper.getMainLooper()时可以获取到主线程的Looper对象了。主线程Handler初始化在main方法下创建了ActivityThread对象,并获取了主线程的Handler。final H mH = new H(); final Handler getHandler() { return mH; }由此可见主线程的Handler作为ActivityThread的成员变量,是在ActivityThread的main方法被执行,ActivityThread被创建时而初始化。ActivityThread.attch()frameworks/base/core/java/android/app/ActivityThread.javafinal ApplicationThread mAppThread = new ApplicationThread(); private void attach(boolean system) { ...... final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } ...... }ApplicationThread是ActivityThread内部类,继承自IApplicationThread.Stub,作为服务端接受AMS发出的请求并执行,ApplicationThread是ActivityThread与AMS连接的桥梁。attch方法实际调用了AMS的attachApplication方法,去看下AMS里的实现:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java@Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }AMS中的attachApplicationLocked方法有些复杂,这个流程可以理解为ActivityThread创建时(App启动时)需要向AMS注册自己,用于AMS管理ActivityThread中的所有四大组件的生命周期。我们看下attachApplicationLocked中的关键部分:private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ... //1.创建Application thread.bindApplication(...); ... //2.创建Activity if (mStackSupervisor.attachApplicationLocked(app)) { ... } }thread.bindApplicationAMS通过远程调用,最终又会调用到ActivityThread中ApplicationThread内部类方法。public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager ServiceManager.initServiceCache(services); } setCoreSettings(coreSettings); AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; data.providers = providers; data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.instrumentationUiAutomationConnection = instrumentationUiConnection; data.debugMode = debugMode; data.enableBinderTracking = enableBinderTracking; data.trackAllocation = trackAllocation; data.restrictedBackupMode = isRestrictedBackupMode; data.persistent = persistent; data.config = config; data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; sendMessage(H.BIND_APPLICATION, data); }在方法中将AMS传递回的数据又发送给主线程中case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;在主线程中调用handleBindApplication处理:private void handleBindApplication(AppBindData data) { ...... // send up app name; do this *before* waiting for debugger //设置进程名, 也就是说进程名是在进程真正创建以后的BIND_APPLICATION过程中才取名 Process.setArgV0(data.processName); android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId()); //重置时区 TimeZone.setDefault(null); LocaleList.setDefault(data.config.getLocales()); //获取LoadedApk对象 // (public final LoadedApk getPackageInfoNoCheck()) data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ...... try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } mInstrumentation.onCreate(data.instrumentationArgs); //调用Application.onCreate()回调方法 mInstrumentation.callApplicationOnCreate(app); } finally { StrictMode.setThreadPolicy(savedPolicy); } }handleBindApplication中调用LoadedApk类中makeApplication方法:public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { // 表示Application是个单例 if (mApplication != null) { return mApplication; } Application app = null; // 通过反射初始化Application String appClass = mApplicationInfo.className; if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } try { java.lang.ClassLoader cl = getClassLoader(); if (!mPackageName.equals("android")) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { if (!mActivityThread.mInstrumentation.onException(app, e)) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); throw new RuntimeException( "Unable to instantiate application " + appClass + ": " + e.toString(), e); } } mActivityThread.mAllApplications.add(app); mApplication = app; ...... return app; }到这里应用的Application 就创建出来了,创建Application后再调用callApplicationOnCreate,回调Application的onCreate方法。mStackSupervisor.attachApplicationLocked(app)mStackSupervisor是AMS的成员变量,是Activity堆栈管理辅助类实例frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.javaboolean attachApplicationLocked(ProcessRecord app) throws RemoteException { final String processName = app.processName; boolean didSomething = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (!isFocusedStack(stack)) { continue; } ActivityRecord hr = stack.topRunningActivityLocked(); if (hr != null) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { if (realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting activity " + hr.intent.getComponent().flattenToShortString(), e); throw e; } } } } } if (!didSomething) { ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } return didSomething; }attachApplicationLocked中获取App要启动的top Activity,然后realStartActivityLocked去启动。final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ...... app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); ...... }调用ApplicationThread的scheduleLaunchActivity方法:@Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) ....... sendMessage(H.LAUNCH_ACTIVITY, r); } case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);最终调用handleLaunchActivity,最终启动一个Activityprivate void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { //通过反射去创建一个Activity,然后会调用Activity的各个生命周期方法 Activity a = performLaunchActivity(r, customIntent); if (a != null) { r.createdConfig = new Configuration(mConfiguration); reportSizeConfigurations(r); Bundle oldState = r.state; // onResume handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); if (!r.activity.mFinished && r.startsNotResumed) { performPauseActivityIfNeeded(r, reason); if (r.isPreHoneycomb()) { r.state = oldState; } } } else { // If there was an error, for any reason, tell the activity manager to stop us. try { ActivityManagerNative.getDefault() .finishActivity(r.token, Activity.RESULT_CANCELED, null, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } }private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // 从ActivityClientRecord中获取待启动的Activity的组件信息 ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ComponentName component = r.intent.getComponent(); if (component == null) { component = r.intent.resolveActivity( mInitialApplication.getPackageManager()); r.intent.setComponent(component); } if (r.activityInfo.targetActivity != null) { component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } Activity activity = null; try { //反射创建Activity java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { //再次调用Application的创建方法,Application是个单例。但是如果这个Activity在另一个进程中,就需要再次创建Application Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false; // 调用Activity的onCreate方法回调,终于看到熟悉的Activity onCreate啦 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } r.activity = activity; r.stopped = true; if (!r.activity.mFinished) { // onStart回调 activity.performStart(); r.stopped = false; } if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnPostCreate(activity, r.state); } if (!activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); } } } r.paused = true; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
2023年01月05日
567 阅读
0 评论
1 点赞
2023-01-05
Android之WMS
WindowManagerService简称WMS,WMS是属于Android系统的其他服务,但是它的重要性和AMS在同一级别,作为“窗口管理员”,它负责窗口的创建、调度、显示以及触摸事件分发。也是一个Android程序猿必须了解的系统服务。在SyetemServer启动时会调用startOtherServices:private void startOtherServices() { WindowManagerService wm = null; //InputManagerService traceBeginAndSlog("StartInputManagerService"); inputManager = new InputManagerService(context); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); traceBeginAndSlog("StartWindowManagerService"); wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); wm.displayReady(); wm.systemReady(); }可以看到,InputManagerService作为参数传递给了WMS,而WMS又作为参数传给了AMSWMS.mainpublic static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; //运行在"android.display"线程 DisplayThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { holder[0] = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, onlyCore); } }, 0); return holder[0]; }WindowManagerServiceprivate WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) { mContext = context; mHaveInputMethods = haveInputMethods; mAllowBootMessages = showBootMsgs; mOnlyCore = onlyCore; ...... //保存传进来的IMS mInputManager = inputManager; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); mDisplaySettings.readSettingsLocked(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); // 获取Display数组,每个显示设备对应一个Display mDisplays = mDisplayManager.getDisplays(); for (Display display : mDisplays) { // 创建DisplayContent,用于支持多屏幕的功能 createDisplayContentLocked(display); } mActivityManager = ActivityManagerNative.getDefault(); //创建WindowAnimator,用于管理所有的窗口动画 mAnimator = new WindowAnimator(this); mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); LocalServices.addService(WindowManagerInternal.class, new LocalService()); //初始化窗口管理策略接口类WindowManagerPolicy initPolicy(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); ...... }initPolicyprivate void initPolicy() { //运行在"android.ui"线程 UiThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); }在initPolicy中,mPolicy实际为PhoneWindowManager。final WindowManagerPolicy mPolicy = new PhoneWindowManager();"system_server"线程中会调用WMS的main方法,main方法中会创建WMS,创建WMS的过程运行在"android.display"线程中,它的优先级更高一些,因此要等创建WMS完毕后才会唤醒处于等待状态的"system_server"线程。WMS初始化时会执行initPolicy方法,initPolicy方法会调用PWM的init方法,这个init方法运行在"android.ui"线程,并且优先级更高,因此要先执行完PWM的init方法后,才会唤醒处于等待状态的"android.display"线程。
2023年01月05日
970 阅读
0 评论
0 点赞
2023-01-04
Android之Init
init是Linux系统中用户控件的第一个进程,init的职责:1.init进程创建系统中的几个关键进程,如zygote进程2.Android系统中有很多属性,于是init提供了一个property service来管理它们。init的工作流程:解析两个配置文件,我们将分析其中对init.rc文件的解析执行各个阶段的动作,创建zygote的工作就是在其中的某个阶段完成的调用property_init初始化属性相关的资源,并且通过property_start_server启动属性服务init进入一个无限循环,并且等待一些事情发生。重点关注init如何处理来自socket和来自属性服务器的相关事情解析配置文件: init会解析两个配置文件,一个是init.rc,另一个与硬件平台相关。解析配置文件调用的parse_config_file函数。这个函数中主要是读取配置文件(read_file),读取结束后调用parse_config函数进行真正的解析keyword.h文件中定义了init中使用关键字通过两次包含得到了一个keyword_info结构体数组。zygote是由init进程fork并且execv(这时会传入一个环境变量,在ZygoteInit.java的main方法中会用到)后创建的。zygote进程死后、它的父进程init会找到代表zygote的那个service并且杀掉zygote创建的所有子进程,Java世界也就崩溃了zygote的重启,zygote死的时候会将service结构体的flags设置为SVC_RESTARTING;inti.c::main函数中有一个代码片段zygote就重启for(;;){ int nr, i, timeout = -1; for (i = 0; i < fd_count; i++} ufds[i].revents = 0; drain_action_queue () ; //poll A数返回后,会进入下一轮的循环 restart_processes () ; // 这里会重启所有 flag 标志为 SVC_RESTARTING的 service。 }init.rc是一个配置文件,是由Android初始化语言编写的脚本,主要包含五种类型语句:Action、Command、Service、Option、ImportAction动作由一组命令(Commands)组成,动作还包含了一个触发器,决定了运行这个动作的时机。通过触发器trigger,即通过以on开头的语句来决定执行相应的service的时机,具体有如下时机:on early-init; 在初始化早期阶段触发;on init; 在初始化阶段触发;on late-init; 在初始化晚期阶段触发;on boot/charger: 当系统启动/充电时触发;on property:<key>=<value>: 当属性值满足条件时触发;CommandCommand是action 的命令列表中命令,或者是service中的选项onrestart的参数命令,命令将在所属事件发生时被一个一个的执行。常用命令如下:class_start <service_class_name>: 启动属于同一个class的所有服务;class_stop <service_class_name> : 停止指定类的服务start <service_name>: 启动指定的服务,若已启动则跳过;stop <service_name>: 停止正在运行的服务setprop <name> <value>:设置属性值mkdir <path>:创建指定目录symlink <target> <sym_link>: 创建连接到<target>的<sym_link>符号链接;write <path> <string>: 向文件path中写入字符串;exec: fork并执行,会阻塞init进程直到程序完毕;exprot <name> <name>:设定环境变量;loglevel <level>:设置log级别hostname <name> : 设置主机名import <filename> :导入一个额外的init配置文件Service服务Service,以service开头,由init进程启动,一般运行在init的一个子进程中,所以启动service前需要判断对应的可执行文件是否存在。命令:service <name><pathname> [ <argument> ]* <option> <option><name>:表示service的名称<pathname>:表示此 service 所在的路径( service 为可执行文件,所以存在存储路径)<argument>:表示启动 service 所带的参数<option>:表示对此 service 的约束选项init生成的子进程,定义在rc文件,其中每一个service在启动时会通过fork方式生成子进程。OptionOptions是Service的可选项,与service配合使用disabled: 不随class自动启动,只有根据service名才启动;oneshot: service退出后不再重启;user/group: 设置执行服务的用户/用户组,默认都是root;class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default;onrestart:当服务重启时执行相应命令;socket: 创建名为/decdcritical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式default: 意味着 disabled=false,oneshot=false,critical=falseImport用来导入其他的 rc 文件命令:import <filename>init.rc实例从Android5.0的版本开始,Android支持64位的编译,因此Zygote本身也支持32位和64位。通过属性ro.zygote来控制不同版本的zygote进程启动。init.rc位于"/system/core/rootdir"目录下,在这个路径下还包括四个关于zygote的rc文件。分别是init.zygote32.rc,init.zygote32_64.rc,init.zygote64.rc,init.zygote64_32.rc,由硬件决定调用哪个文件。import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /vendor/etc/init/hw/init.${ro.hardware}.rc import /init.usb.configfs.rc import /init.${ro.zygote}.rc on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000
2023年01月04日
940 阅读
0 评论
0 点赞
2023-01-03
Android之Zygote
Zygote是Android系统创建新进程的核心进程,负责启动Dalvik虚拟机,加载一些必要的系统资源和系统类,启动system_server进程,随后进入等待处理app应用请求。Zygote进程由init启动:# /vendor/default.prop ro.zygote=zygote64_32 # /init.rc import /init.${ro.zygote}.rc on zygote-start && property:ro.crypto.state=... # A/B update verifier that marks a successful boot. exec_start update_verifier_nonencrypted start netd start zygote start zygote_secondary # /init.zygote64_32.rc service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload class main priority -20 user root group root readproc reserved_disk socket zygote_secondary stream 660 root system onrestart restart zygote writepid /dev/cpuset/foreground/tasks 属性 ro.zygote 的值包括zygote32、zygote64、zygote32_64、zygote64_32,对应区别如下:init.zygote32.rc:zygote进程对应的执行程序是app_process(纯32bit模式)init.zygote64.rc:zygote进程对应的执行程序是app_process64(纯64bit模式)init.zygote32_64.rc:启动两个zygote进程,对应的执行程序分别是app_process32(主模式)、app_process64init.zygote64_32.rc:启动两个zygote进程,对应的执行程序分别是app_process64(主模式)、app_process32zygote和zygote_secondary其实大同小异,都是执行/system/bin/app_process,其执行的的应用及参数如下:app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygotemain() AppRuntime::AppRuntime() AndroidRuntime::AndroidRuntime() SkGraphics::Init() // 创建Dalvik缓存 maybeCreateDalvikCache() AndroidRuntime::start("com.android.internal.os.ZygoteInit", args, ) // JniInvocation位于libnativehelper JniInvocation::JniInvocation() /* * 初始化虚拟机环境 * - 加载libart.so, 由art/runtime生成 * - 导出JNI_GetDefaultJavaVMInitArgs * - 导出JNI_CreateJavaVM * - 导出JNI_GetCreatedJavaVMs */ JniInvocation::Init(NULL) AndroidRuntime::startVm(JavaVM, JNIEnv,) // 获取虚拟机参数 AndroidRuntime::parseRuntimeOption() // 位于libnativehelper ??? JNI_CreateJavaVM() JniInvocation::JNI_CreateJavaVM() AppRuntime::onVmCreated() // 向VM注册native函数 AndroidRuntime::startReg() /* * 依次注册预定义的gRegJNI列表, 包括 * frameworks/base/core/jni/android_xxx.cpp * frameworks/base/core/jni/com_xxx.cpp * 下面以android_util_Process.cpp为例 */ register_jni_procs(gRegJNI) register_android_os_Process() /* * 注册 android.os.Process 类 * 注册 android.os.Process.setUid 方法 * ... * * static const JNINativeMethod methods[] = { * ... * {"setUid", "(I)I", (void*)android_os_Process_setUid}, * ... * }; */ RegisterMethodsOrDie(env, "android/os/Process", methods,) AndroidRuntime::registerNativeMethods(env, className, methods, ) jniRegisterNativeMethods() JNINativeInterface::RegisterNatives() /* * 找到入口com.android.internal.os.ZygoteInit(在register_jni_procs中注册) */ JNIEnv::FindClass("com/android/internal/os/ZygoteInit") // 找到入口类的main函数 JNIEnv::GetStaticMethodID(jclass, "main") // 执行com.android.internal.os.ZygoteInit.main() JNIEnv::CallStaticVoidMethod(jclass, jmethodID, jobjectArray) 接下来就是ZygoteInit的执行过程ZygoteInit.main() /* * 在init.zygote*.rc中注册了zygote套接字 * init进程在启动service时会添加环境变量 * 环境变量: "ANDROID_SOCKET_zygote" * * 从环境变量中获取socket的fd * 通过LocalServerSocket()创建服务端 */ ZygoteServer::registerServerSocketFromEnv("zygote") // FIXME: Add More ZygoteInit::preload() // 主动进行GC操作 ZygoteInit::gcAndFinalize() Zygote::nativeSecurityInit() com_android_internal_os_Zygote_nativeSecurityInit() Zygote::nativeUnmountStorageOnInit() com_android_internal_os_Zygote_nativeUnmountStorageOnInit() /* * 启动SystemServer, 重命名为system_server */ ZygoteInit::forkSystemServer() Zygote::forkSystemServer() Zygote::nativeForkSystemServer() com_android_internal_os_Zygote_nativeForkSystemServer() ForkAndSpecializeCommon() fork() // 子进程: com.android.internal.os.Zygote JNIENV::CallStaticVoidMethod("com/android/internal/os/Zygote") // 子进程: FIXME: Add More ZygoteInit::handleSystemServerProcess() ZygoteInit::zygoteInit(, "com.android.server.SystemServer", ) RuntimeInit::commonInit() ZygoteInit::nativeZygoteInit() com_android_internal_os_ZygoteInit_nativeZygoteInit() AppRuntime::onZygoteInit() RuntimeInit::applicationInit() RuntimeInit::findStaticMain("com.android.server.SystemServer", , ) // 执行com.android.server.SystemServer.main() MethodAndArgsCaller.run() /* * 监听zygote socket * 等待客户端消息并处理 * ZygoteConnection用于表示和客户端的连接 */ ZygoteServer::runSelectLoop() ZygoteConnection::processOneCommand() app_process有两种启动模式,都是调用AppRuntime::start(),加载ZygoteInit或RuntimeInit两个Java类Zygote模式: 即初始化zygote进程,也即上面分析的流程Application模式: 即启动普通应用程序,传递的参数有class名字以及class带的参数Zygote启动过程中fork了一个新进程用于启动com.android.server.SystemServer,即SystemServer,文件路径如下:frameworks/base/services/java/com/android/server/SystemServer.javaSystemServer(进程名为system_server)是android服务的提供者,所有service运行在该进程中,主要流程如下:SystemServer::main() SystemServer::run() /* * 一些准备工作 */ Looper.prepareMainLooper() /* * 初始化native服务 * libandroid_servers * <- libservices.core * 由[frameworks/base/services/core/jni/*]编译生成 */ System::loadLibrary("android_servers"); // 位于frameworks/base/services/core/jni/onload.cpp JNI_OnLoad() register_android_server_broadcastradio_BroadcastRadioService() register_android_server_broadcastradio_Tuner() register_android_server_PowerManagerService() /* * 向com.android.server.power.PowerManagerService注册native方法 * 2rd参数: "com/android/server/power/PowerManagerService" * 3rd参数: gPowerManagerServiceMethods * 4rd参数: NELEM(gPowerManagerServiceMethods) */ jniRegisterNativeMethods(JNIEnv, , ,) register_android_server_SerialService() ... // FIXME SystemServer::performPendingShutdown() // 初始化系统上下文 SystemServer::createSystemContext() ActivityThread::systemMain() new ActivityThread() // FIXME: a lot thing seems done ActivityThread::attach(true, 0) ActivityThread::getSystemContext() mSystemServiceManager = new SystemServiceManager() /* * 区别于BinderService, Localservice只在本进程使用 */ LocalServices.addService(SystemServiceManager.class, mSystemServiceManager) /* * 启动关键服务 */ startBootstrapServices(); // FIXME: 读取系统配置 SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, ) /* * 启动[Installer]服务并连接至installd * installd为native服务, 位于frameworks/native/cmds/installd/ */ SystemServiceManager::startService(Installer.class) new Installer() && Installer::onStart() // 启动[设备标识符]服务 SystemServiceManager::startService(DeviceIdentifiersPolicyService.class) new DeviceIdentifiersPolicyService() DeviceIdentifiersPolicyService::onStart() // 启动[AMS]服务 SystemServiceManager::startService(ActivityManagerService.Lifecycle.class) new ActivityManagerService() ActivityManagerService::onStart() // 启动[电源管理服务] SystemServiceManager::startService(PowerManagerService.class) new PowerManagerService() PowerManagerService::onStart() publishBinderService(Context.POWER_SERVICE, new BinderService()); // 向servicemanager注册服务 ServiceManager.addService( , , , ) publishLocalService(PowerManagerInternal.class, new LocalService()); LocalServices.addService( , ) // 初始化电源管理功能 ActivityManagerService::initPowerManagement() SystemServiceManager::startService(RecoverySystemService.class) SystemServiceManager::startService(LightsService.class) // 启动[显示管理服务] SystemServiceManager::startService(DisplayManagerService.class) // 等待默认显示器 SystemServiceManager::startBootPhase(SystemService.PHASE_WAIT_FOR_DE~T_DISPLAY); DisplayManagerService::onBootPhase(SystemService.PHASE_WAIT_FOR_DE~T_DISPLAY) // 启动[PackageManagerService, PMS]服务 PackageManagerService::main(mSystemContext, installer, , ) ActivityManagerService::setSystemProcess() new OverlayManagerService(mSystemContext, installer) SystemServiceManager::startService(mOverlayManagerService) startSensorService() /* * 启动必要服务 */ startCoreServices(); SystemServiceManager::startService(BatteryService.class) SystemServiceManager::startService(UsageStatsService.class) BinderCallsStatsService.start(); /* * 启动其他服务, 太多了, 这里不一一列举 */ startOtherServices(); /* * 进入循环 */ Looper.loop();
2023年01月03日
371 阅读
0 评论
0 点赞
2023-01-03
Android之PMS
PMS全称PackageManagerService,PMS是Android系统核心服务之一,处理包管理相关的工作,常见的比如安装、卸载应用等。PMS是系统服务,那么应用层肯定有个PackageManager作为binder call client端来供使用,但是这里要注意,PackageManager是个抽象类,一般使用的是它的实现类:ApplicationPackageManager。因此PackageManager功能的具体实现还是ApplicationPackageManager这个实现类。PMS主要功能:安装、卸载应用查询permission相关信息查询Application相关信息(application、activity、receiver、service、provider及相应属性等)查询已安装应用增加、删除permission清除用户数据、缓存、代码等在SystemServer启动时会调用startBootstrapServices()来启动引导服务,在startBootstrapServices中会启动PackageManagerService:... mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); 这里PMS的构造方法:public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { // Self-check for initial settings. PackageManagerServiceCompilerMapping.checkProperties(); //初始化PMS PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); m.enableSystemUserPackages(); //将package服务注册到ServiceManager大管家 ServiceManager.addService("package", m); return m; }PMS的构造方法一共有700多行,在代码中,将PMS的构造流程分为了5个阶段,每个阶段会使用EventLog.writeEvent打印系统日志。BOOT_PROGRESS_PMS_START(开始阶段)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(扫描系统阶段)BOOT_PROGRESS_PMS_DATA_SCAN_START(扫描Data分区阶段)BOOT_PROGRESS_PMS_SCAN_END(扫描结束阶段)BOOT_PROGRESS_PMS_READY(准备阶段)开始阶段(BOOT_PROGRESS_PMS_START)public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { //打印开始日志 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); if (mSdkVersion <= 0) { Slog.w(TAG, "**** ro.build.version.sdk not set!"); } mContext = context; mPermissionReviewRequired = context.getResources().getBoolean( R.bool.config_permissionReviewRequired); mFactoryTest = factoryTest; mOnlyCore = onlyCore; //用于存储屏幕的相关信息 mMetrics = new DisplayMetrics(); //创建Settings对象 (1) mSettings = new Settings(mPackages); // 添加system, phone, log, nfc, bluetooth, shell这六种shareUserId到mSettings; mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); ...... mInstaller = installer; //创建Dex优化工具类 mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*"); mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); mOnPermissionChangeListeners = new OnPermissionChangeListeners( FgThread.get().getLooper()); getDefaultDisplayMetrics(context, mMetrics); //得到全局系统配置信息 SystemConfig systemConfig = SystemConfig.getInstance(); //获取全局的groupId mGlobalGids = systemConfig.getGlobalGids(); //获取系统权限 mSystemPermissions = systemConfig.getSystemPermissions(); mAvailableFeatures = systemConfig.getAvailableFeatures(); mProtectedPackages = new ProtectedPackages(mContext); //安装APK时需要的锁,保护所有对installd的访问。 synchronized (mInstallLock) { //更新APK时需要的锁,保护内存中已经解析的包信息等内容 synchronized (mPackages) { //创建后台线程ServiceThread mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); //创建PackageHandler绑定到ServiceThread的消息队列 mHandler = new PackageHandler(mHandlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); //将PackageHandler添加到Watchdog的检测集中 Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this); //在Data分区创建一些目录 File dataDir = Environment.getDataDirectory(); mAppInstallDir = new File(dataDir, "app"); mAppLib32InstallDir = new File(dataDir, "app-lib"); mEphemeralInstallDir = new File(dataDir, "app-ephemeral"); mAsecInternalPath = new File(dataDir, "app-asec").getPath(); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); mRegionalizationAppInstallDir = new File(dataDir, "app-regional"); //创建多用户管理服务 sUserManager = new UserManagerService(context, this, mPackages); mFoundPolicyFile = SELinuxMMAC.readInstallPolicy(); //解析packages.xml等文件的信息,保存到Settings的对应字段中。packages.xml中记录系统中所有安装的应用信息,包括基本信息、签名和权限。如果packages.xml有安装的应用信息,readLPw方法会返回true,mFirstBoot的值为false,说明PMS不是首次被启动。 mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));mSettings 用于保存所有包的动态设置Settings(Object lock) { this(Environment.getDataDirectory(), lock); } Settings(File dataDir, Object lock) { mLock = lock; mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock); mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); //创建/data/system FileUtils.setPermissions(mSystemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1); mSettingsFilename = new File(mSystemDir, "packages.xml"); mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml"); mPackageListFilename = new File(mSystemDir, "packages.list"); FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID); mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); }mSystemDir是指目录/data/system,有5个子文件:packages.xml 记录所有安装app的信息packages-backup.xml 备份文件packages-stopped.xml 记录系统被强制停止的文件packages-stopped-backup.xml 备份文件packages.list 记录应用的数据信息mInstallerInstaller继承自SystemService,和PMS、AMS一样是系统的服务(引导服务),PMS很多的操作都是由Installer来完成的,比如APK的安装和卸载。在Installer内部,通过socket与installd通信,8.0以上改成了IInstalld和installd进行Binder通信,由位于nativie层的installd来完成具体的操作。systemConfig用于得到全局系统配置信息。比如系统的权限就可以通过SystemConfig来获取。mPackageDexOptimizerDex优化的工具类。mHandler(PackageHandler类型)PackageHandler继承自Handler,PMS通过PackageHandler驱动APK的复制和安装工作。PackageHandler处理的消息队列如果过于繁忙,有可能导致系统卡住, 因此将它添加到Watchdog的监测集中。Watchdog主要有两个用途,一个是定时检测系统关键服务(AMS和WMS等)是否可能发生死锁,还有一个是定时检测线程的消息队列是否长时间处于工作状态(可能阻塞等待了很长时间)。如果出现上述问题,Watchdog会将日志保存起来,必要时还会杀掉自己所在的进程,也就是SystemServer进程。sUserManager(UserManagerService类型)多用户管理服务2、扫描系统阶段(BOOT_PROGRESS_PMS_SYSTEM_SCAN_START)//打印扫描系统阶段日志 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime); // Set flag to monitor and not change apk file paths when // scanning install directories. final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL; final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); if (bootClassPath == null) { Slog.w(TAG, "No BOOTCLASSPATH found!"); } if (systemServerClassPath == null) { Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); } final List<String> allInstructionSets = InstructionSets.getAllInstructionSets(); final String[] dexCodeInstructionSets = getDexCodeInstructionSets( allInstructionSets.toArray(new String[allInstructionSets.size()])); /** * Ensure all external libraries have had dexopt run on them. */ if (mSharedLibraries.size() > 0) { // NOTE: For now, we're compiling these system "shared libraries" // (and framework jars) into all available architectures. It's possible // to compile them only when we come across an app that uses them (there's // already logic for that in scanPackageLI) but that adds some complexity. for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (SharedLibraryEntry libEntry : mSharedLibraries.values()) { final String lib = libEntry.path; if (lib == null) { continue; } try { // Shared libraries do not have profiles so we perform a full // AOT compilation (if needed). int dexoptNeeded = DexFile.getDexOptNeeded( lib, dexCodeInstructionSet, getCompilerFilterForReason(REASON_SHARED_APK), false /* newProfile */); if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet, dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/, getCompilerFilterForReason(REASON_SHARED_APK), StorageManager.UUID_PRIVATE_INTERNAL, SKIP_SHARED_LIBRARY_CHECK); } } catch (FileNotFoundException e) { Slog.w(TAG, "Library not found: " + lib); } catch (IOException | InstallerException e) { Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " + e.getMessage()); } } } } //在/system中创建framework目录 File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion(); mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; // When upgrading from pre-N, we need to handle package extraction like first boot, // as there is no profiling data available. mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N; mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; // save off the names of pre-existing system packages prior to scanning; we don't // want to automatically grant runtime permissions for new system apps if (mPromoteSystemApps) { Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator(); while (pkgSettingIter.hasNext()) { PackageSetting ps = pkgSettingIter.next(); if (isSystemApp(ps)) { mExistingSystemPackages.add(ps.name); } } } // Collect vendor overlay packages. (Do this before scanning any apps.) // For security and version matching reason, only consider // overlay packages if they reside in the right directory. String overlayThemeDir = SystemProperties.get(VENDOR_OVERLAY_THEME_PROPERTY); //扫描/vendor/overlay目录下的文件 if (!overlayThemeDir.isEmpty()) { scanDirTracedLI(new File(VENDOR_OVERLAY_DIR, overlayThemeDir), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0); } scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0); // Find base frameworks (resource packages without code). //收集包名:/system/framework scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0); // Collected privileged system packages. //收集私有的系统包名:/system/priv-app final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); scanDirTracedLI(privilegedAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0); // Collect ordinary system packages. //收集一般的系统包名:/system/app final File systemAppDir = new File(Environment.getRootDirectory(), "app"); scanDirTracedLI(systemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all vendor packages. //收集所有的供应商包名:/vendor/app File vendorAppDir = new File("/vendor/app"); try { vendorAppDir = vendorAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(vendorAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all OEM packages. //收集所有OEM包名:/oem/app final File oemAppDir = new File(Environment.getOemDirectory(), "app"); scanDirTracedLI(oemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all Regionalization packages form Carrier's res packages. if (RegionalizationEnvironment.isSupported()) { Log.d(TAG, "Load Regionalization vendor apks"); final List<File> RegionalizationDirs = RegionalizationEnvironment.getAllPackageDirectories(); for (File f : RegionalizationDirs) { File RegionalizationSystemDir = new File(f, "system"); // Collect packages in <Package>/system/priv-app scanDirLI(new File(RegionalizationSystemDir, "priv-app"), PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0); // Collect packages in <Package>/system/app scanDirLI(new File(RegionalizationSystemDir, "app"), PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect overlay in <Package>/system/vendor scanDirLI(new File(RegionalizationSystemDir, "vendor/overlay"), PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0); } } // Prune any system packages that no longer exist. // 这个列表代表有可能有升级包的系统App final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>(); if (!mOnlyCore) { Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { PackageSetting ps = psit.next(); /* * If this is not a system app, it can't be a * disable system app. */ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { continue; } /* * If the package is scanned, it's not erased. */ final PackageParser.Package scannedPkg = mPackages.get(ps.name); if (scannedPkg != null) { /* * If the system app is both scanned and in the * disabled packages list, then it must have been * added via OTA. Remove it from the currently * scanned package so the previously user-installed * application can be scanned. */ if (mSettings.isDisabledSystemPackageLPr(ps.name)) { //1 logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known codePath=" + ps.codePathString + ", installStatus=" + ps.installStatus + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.mVersionCode); //将这个系统App的PackageSetting从PMS的mPackages中移除 removePackageLI(scannedPkg, true); //将升级包的路径添加到mExpectingBetter列表中 mExpectingBetter.put(ps.name, ps.codePath); } continue; } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { psit.remove(); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); // Actual deletion of code and data will be handled by later // reconciliation step } else { final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); //这个系统App升级包信息在mDisabledSysPackages中,但是没有发现这个升级包存在 if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {//2 possiblyDeletedUpdatedSystemApps.add(ps.name); } } } } //look for any incomplete package installations //清理所有安装不完整的包 ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); for (int i = 0; i < deletePkgsList.size(); i++) { // Actual deletion of code and data will be handled by later // reconciliation step final String packageName = deletePkgsList.get(i).name; logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + packageName); synchronized (mPackages) { mSettings.removePackageLPw(packageName); } } //delete tmp files //删除临时文件 deleteTempPackageFiles(); // Remove any shared userIDs that have no associated packages mSettings.pruneSharedUsersLPw();系统扫描阶段的主要工作有以下3点:创建/system的子目录,比如/system/framework、/system/priv-app和/system/app等等扫描系统文件,比如/vendor/overlay、/system/framework、/system/app等等目录下的文件。对扫描到的系统文件做后续处理。关于第3点,一次OTA升级对于一个系统App会有三种情况:这个系统APP无更新。这个系统APP有更新。新的OTA版本中,这个系统APP已经被删除。当系统App升级,PMS会将该系统App的升级包设置数据(PackageSetting)存储到Settings的mDisabledSysPackages列表中(具体见PMS的replaceSystemPackageLIF方法),mDisabledSysPackages的类型为ArrayMap<String, PackageSetting>。mDisabledSysPackages中的信息会被PMS保存到packages.xml中的<updated-package>标签下(具体见Settings的writeDisabledSysPackageLPr方法)。注释1处说明这个系统App有升级包,那么就将该系统App的PackageSetting从mDisabledSysPackages列表中移除,并将系统App的升级包的路径添加到mExpectingBetter列表中,mExpectingBetter的类型为ArrayMap<String, File>等待后续处理。注释2处如果这个系统App的升级包信息存储在mDisabledSysPackages列表中,但是没有发现这个升级包存在,则将它加入到possiblyDeletedUpdatedSystemApps列表中,意为“系统App的升级包可能被删除”,之所以是“可能”,是因为系统还没有扫描Data分区,只能暂放到possiblyDeletedUpdatedSystemApps列表中,等到扫描完Data分区后再做处理。3、扫描Data分区阶段(BOOT_PROGRESS_PMS_DATA_SCAN_START)//如果设备没有加密,那么就开始扫描Data分区 if (!mOnlyCore) { //打印扫描Data分区阶段日志 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); //扫描/data/app目录下的文件 scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); //扫描/data/app-private目录下的文件 scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags | PackageParser.PARSE_FORWARD_LOCK, scanFlags | SCAN_REQUIRE_KNOWN, 0); //扫描/data/app-ephemeral目录下的文件 scanDirLI(mEphemeralInstallDir, mDefParseFlags | PackageParser.PARSE_IS_EPHEMERAL, scanFlags | SCAN_REQUIRE_KNOWN, 0); /** * Remove disable package settings for any updated system * apps that were removed via an OTA. If they're not a * previously-updated app, remove them completely. * Otherwise, just revoke their system-level permissions. * 处理possiblyDeletedUpdatedSystemApps列表 * */ for (String deletedAppName : possiblyDeletedUpdatedSystemApps) { PackageParser.Package deletedPkg = mPackages.get(deletedAppName); mSettings.removeDisabledSystemPackageLPw(deletedAppName); String msg; if (deletedPkg == null) { //1 如果这个系统App的包信息不在PMS的变量mPackages中,说明是残留的App信息,后续会删除它的数据 msg = "Updated system package " + deletedAppName + " no longer exists; it's data will be wiped"; // Actual deletion of code and data will be handled by later // reconciliation step } else { //2 如果这个系统App在mPackages中,说明是存在于Data分区,不属于系统App,那么移除其系统权限。 msg = "Updated system app + " + deletedAppName + " no longer present; removing system privileges for " + deletedAppName; deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName); deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; } logCriticalInfo(Log.WARN, msg); } /** * Make sure all system apps that we expected to appear on * the userdata partition actually showed up. If they never * appeared, crawl back and revive the system version. */ //遍历mExpectingBetter列表 for (int i = 0; i < mExpectingBetter.size(); i++) { final String packageName = mExpectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { //得到系统App的升级包路径 final File scanFile = mExpectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); //3 根据系统App所在的目录设置扫描的解析参数 int reparseFlags = mDefParseFlags; if (FileUtils.contains(privilegedAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED; } else if (FileUtils.contains(systemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(vendorAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(oemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } //4 将packageName对应的包设置数据(PackageSetting)添加到mSettings的mPackages中 mSettings.enableSystemPackageLPw(packageName); try { //5 扫描系统App的升级包 scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " + e.getMessage()); } } } } //清除mExpectingBetter列表 mExpectingBetter.clear();扫描Data分区阶段主要做了以下几件事:扫描/data/app和/data/app-private目录下的文件。遍历possiblyDeletedUpdatedSystemApps列表,注释1处如果这个系统App的包信息不在PMS的变量mPackages中,说明是残留的App信息,后续会删除它的数据。注释2处如果这个系统App的包信息在mPackages中,说明是存在于Data分区,不属于系统App,那么移除其系统权限。遍历mExpectingBetter列表,注释3处根据系统App所在的目录设置扫描的解析参数,注释4处的方法内部会将packageName对应的包设置数据(PackageSetting)添加到mSettings的mPackages中。注释5处扫描系统App的升级包,最后清除mExpectingBetter列表。4、扫描结束阶段(BOOT_PROGRESS_PMS_SCAN_END)//打印扫描结束阶段日志 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. //当sdk版本不一致时,需要更新权限 int updateFlags = UPDATE_PERMISSIONS_ALL; if (ver.sdkVersion != mSdkVersion) { Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to " + mSdkVersion + "; regranting permissions for internal storage"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); ver.sdkVersion = mSdkVersion; // If this is the first boot or an update from pre-M, and it is a normal // boot, then we need to initialize the default preferred apps across // all defined users. //如果是第一次启动或者是Android M升级后的第一次启动,需要初始化所有用户定义的默认首选App if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : sUserManager.getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(this, user.id); applyFactoryDefaultBrowserLPw(user.id); primeDomainVerificationsLPw(user.id); } } //在引导过程中尽早为系统用户准备存储, //因为核心系统应用程序,如设置Provider和SystemUI无法等待用户启动 final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated()) { storageFlags = StorageManager.FLAG_STORAGE_DE; } else { storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, storageFlags); // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. // Note that we do *not* clear the application profiles. These remain valid // across OTAs and are used to drive profile verification (post OTA) and // profile compilation (without waiting to collect a fresh set of profiles). // OTA后的第一次启动,会清除代码缓存目录 if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (int i = 0; i < mSettings.mPackages.size(); i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) { // No apps are running this early, so no need to freeze clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } } ver.fingerprint = Build.FINGERPRINT; } checkDefaultBrowser(); //当权限和其他默认项都完成更新,则清理相关信息 mExistingSystemPackages.clear(); mPromoteSystemApps = false; // All the changes are done during package scanning. ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION; // 把Settings的内容保存到packages.xml中 mSettings.writeLPr();扫描结束结束阶段主要做了以下几件事:如果当前平台SDK版本和上次启动时的SDK版本不同,重新更新APK的授权。如果是第一次启动或者是Android M升级后的第一次启动,需要初始化所有用户定义的默认首选App。OTA升级后的第一次启动,会清除代码缓存目录。把Settings的内容保存到packages.xml中,这样此后PMS再次创建时会读到此前保存的Settings的内容。5、准备阶段(BOOT_PROGRESS_PMS_READY)//打印准备阶段日志 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); if (!mOnlyCore) { mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); mIntentFilterVerifier = new IntentVerifierProxy(mContext, mIntentFilterVerifierComponent); mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED); } else { mRequiredVerifierPackage = null; if (mOnlyPowerOffAlarm) { mRequiredInstallerPackage = getRequiredInstallerLPr(); } else { mRequiredInstallerPackage = null; } mRequiredUninstallerPackage = null; mIntentFilterVerifierComponent = null; mIntentFilterVerifier = null; mServicesSystemSharedLibraryPackageName = null; mSharedSystemSharedLibraryPackageName = null; } //创建PackageInstallerService,用于管理安装会话的服务,它会为每次安装过程分配一个SessionId mInstallerService = new PackageInstallerService(context, this); //进行一次垃圾收集 untime.getRuntime().gc(); // The initial scanning above does many calls into installd while // holding the mPackages lock, but we're mostly interested in yelling // once we have a booted system. mInstaller.setWarnIfHeld(mPackages); // 将PackageManagerInternalImpl(PackageManager的本地服务)添加到LocalServices中,LocalServices用于存储运行在当前的进程中的本地服务 LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());总结PMS属于引导服务,由SyetemServer启动,PMS启动又分为5个阶段:BOOT_PROGRESS_PMS_START(开始阶段)BOOT_PROGRESS_PMS_SYSTEM_SCAN_START(扫描系统阶段)BOOT_PROGRESS_PMS_DATA_SCAN_START(扫描Data分区阶段)BOOT_PROGRESS_PMS_SCAN_END(扫描结束阶段)BOOT_PROGRESS_PMS_READY(准备阶段)
2023年01月03日
136 阅读
0 评论
0 点赞
2023-01-03
Android之AMS
AMS全称ActivityManagerService,AMS是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在Android中非常重要。统一调度所有应用程序的Activity的生命周期启动或杀死应用程序的进程启动并调度Service的生命周期注册BroadcastReceiver,并接收和分发Broadcast启动并发布ContentProvider调度task处理应用程序的Crash查询系统当前运行状态ActivityManagerService是在SystemServer.java中启动的。SystemServer会根据顺序依次启动各个服务: // Start services. try { traceBeginAndSlog("StartServices"); startBootstrapServices(); startCoreServices(); startOtherServices(); SystemServerInitThreadPool.shutdown(); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } finally { traceEnd(); }执行startBootstrapServices函数:// Activity manager runs the show. traceBeginAndSlog("StartActivityManager"); mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService(); mActivityManagerService.setSystemServiceManager(mSystemServiceManager); mActivityManagerService.setInstaller(installer); traceEnd(); ……这里启动了ActivityManagerService.Lifecycle.class服务, public static final class Lifecycle extends SystemService { private final ActivityManagerService mService; public Lifecycle(Context context) { super(context); mService = new ActivityManagerService(context); } @Override public void onStart() { mService.start(); } public ActivityManagerService getService() { return mService; } }在startService中调用了ActivityManagerService的onStart()方法来启动该服务:public void startService(@NonNull final SystemService service) { // Register it. mServices.add(service); // Start it. long time = System.currentTimeMillis(); try { service.onStart(); } catch (RuntimeException ex) { throw new RuntimeException("Failed to start service " + service.getClass().getName() + ": onStart threw an exception", ex); } warnIfTooLong(System.currentTimeMillis() - time, service, "onStart"); }
2023年01月03日
220 阅读
0 评论
0 点赞
2022-11-17
Android Webrtc之Kurento-group-call
简介Kurento-group-call是一个类似视频会议的官方例子,在每个用户端都会创建N个端点,相比one2one-call/one2many-call要复杂些。在用户之间连接多个客户端,创建一个视频会议;同样,运行之前要安装Kurento媒体服务。先克隆项目,然后运行主类:git clone https://github.com/Kurento/kurento-tutorial-java.git cd kurento-tutorial-java/kurento-group-call运行项目mvn -U clean spring-boot:run \ -Dspring-boot.run.jvmArguments="-Dkms.url=ws://{KMS_HOST}:8888/kurento"功能分析用户进入房间时,会创建一个新的Media,另外会通知其他用户有新用户连接,然后所有参于者将请求服务器接收新参于者的媒体。新用户依次获取所有已连接的参于者列表,并请求服务器接收房间中所有客户端的media。每个客户端都发送自己的media,然后从其他用户那里接收媒体,每个客户端都会有n个端点,房间总共会有n*n个端点。当用户离开房间时,服务器会通知所有客户端。然后,客户端代码请求服务器取消与离开的客户端相关的所有媒体元素。服务端实例化Kurento客户端后,您就可以与Kurento媒体服务器通信并控制其多媒体功能了。GroupCallApp实现了接口WebSocketConfigurer,注册了一个WebSocketHandler用于处理WebSocket请求@EnableWebSocket @SpringBootApplication public class GroupCallApp implements WebSocketConfigurer { @Bean public UserRegistry registry() { return new UserRegistry(); } @Bean public RoomManager roomManager() { return new RoomManager(); } @Bean public CallHandler groupCallHandler() { return new CallHandler(); } @Bean public KurentoClient kurentoClient() { return KurentoClient.create(); } public static void main(String[] args) throws Exception { SpringApplication.run(GroupCallApp.class, args); } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(groupCallHandler(), "/groupcall"); } } CallHandler类实现TextWebSocketHandler处理文本WebSocket请求。这个类的核心部分是方法handleTextMessage。此方法实现请求的操作,通过WebSocket返回响应public class CallHandler extends TextWebSocketHandler { private static final Logger log = LoggerFactory.getLogger(CallHandler.class); private static final Gson gson = new GsonBuilder().create(); @Autowired private RoomManager roomManager; @Autowired private UserRegistry registry; @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { final JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class); final UserSession user = registry.getBySession(session); if (user != null) { log.debug("Incoming message from user '{}': {}", user.getName(), jsonMessage); } else { log.debug("Incoming message from new user: {}", jsonMessage); } switch (jsonMessage.get("id").getAsString()) { case "joinRoom": joinRoom(jsonMessage, session); break; case "receiveVideoFrom": final String senderName = jsonMessage.get("sender").getAsString(); final UserSession sender = registry.getByName(senderName); final String sdpOffer = jsonMessage.get("sdpOffer").getAsString(); user.receiveVideoFrom(sender, sdpOffer); break; case "leaveRoom": leaveRoom(user); break; case "onIceCandidate": JsonObject candidate = jsonMessage.get("candidate").getAsJsonObject(); if (user != null) { IceCandidate cand = new IceCandidate(candidate.get("candidate").getAsString(), candidate.get("sdpMid").getAsString(), candidate.get("sdpMLineIndex").getAsInt()); user.addCandidate(cand, jsonMessage.get("name").getAsString()); } break; default: break; } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { ... } private void joinRoom(JsonObject params, WebSocketSession session) throws IOException { ... } private void leaveRoom(UserSession user) throws IOException { ... } }在afterConnectionClosed方法里,它会将用户userSession从registry房间中移除并驱逐出去@Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { UserSession user = registry.removeBySession(session); roomManager.getRoom(user.getRoomName()).leave(user); }在joinRoom方法中,服务器检查是否有指定名称的已注册房间,将用户添加到该房间并注册用户private void joinRoom(JsonObject params, WebSocketSession session) throws IOException { final String roomName = params.get("room").getAsString(); final String name = params.get("name").getAsString(); log.info("PARTICIPANT {}: trying to join room {}", name, roomName); Room room = roomManager.getRoom(roomName); final UserSession user = room.join(name, session); registry.register(user); }leaveRoom方法结束一个用户的视频通话private void leaveRoom(UserSession user) throws IOException { final Room room = roomManager.getRoom(user.getRoomName()); room.leave(user); if (room.getParticipants().isEmpty()) { roomManager.removeRoom(room); } }客户端创建WebSocket,在onMessage方法下监听JSON信令协议。有5个不用的消息传入到客户端:existingParticipants,newParticipantArrived,participantLeft,receiveVideoAnswer和iceCandidate。var ws = new WebSocket('wss://' + location.host + '/groupcall'); var participants = {}; var name; window.onbeforeunload = function() { ws.close(); }; ws.onmessage = function(message) { var parsedMessage = JSON.parse(message.data); console.info('Received message: ' + message.data); switch (parsedMessage.id) { case 'existingParticipants': onExistingParticipants(parsedMessage); break; case 'newParticipantArrived': onNewParticipant(parsedMessage); break; case 'participantLeft': onParticipantLeft(parsedMessage); break; case 'receiveVideoAnswer': receiveVideoResponse(parsedMessage); break; case 'iceCandidate': participants[parsedMessage.name].rtcPeer.addIceCandidate(parsedMessage.candidate, function (error) { if (error) { console.error("Error adding candidate: " + error); return; } }); break; default: console.error('Unrecognized message', parsedMessage); } } function register() { name = document.getElementById('name').value; var room = document.getElementById('roomName').value; document.getElementById('room-header').innerText = 'ROOM ' + room; document.getElementById('join').style.display = 'none'; document.getElementById('room').style.display = 'block'; var message = { id : 'joinRoom', name : name, room : room, } sendMessage(message); } function onNewParticipant(request) { receiveVideo(request.name); } function receiveVideoResponse(result) { participants[result.name].rtcPeer.processAnswer (result.sdpAnswer, function (error) { if (error) return console.error (error); }); } function callResponse(message) { if (message.response != 'accepted') { console.info('Call not accepted by peer. Closing call'); stop(); } else { webRtcPeer.processAnswer(message.sdpAnswer, function (error) { if (error) return console.error (error); }); } } function onExistingParticipants(msg) { var constraints = { audio : true, video : { mandatory : { maxWidth : 320, maxFrameRate : 15, minFrameRate : 15 } } }; console.log(name + " registered in room " + room); var participant = new Participant(name); participants[name] = participant; var video = participant.getVideoElement(); var options = { localVideo: video, mediaConstraints: constraints, onicecandidate: participant.onIceCandidate.bind(participant) } participant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function (error) { if(error) { return console.error(error); } this.generateOffer (participant.offerToReceiveVideo.bind(participant)); }); msg.data.forEach(receiveVideo); } function leaveRoom() { sendMessage({ id : 'leaveRoom' }); for ( var key in participants) { participants[key].dispose(); } document.getElementById('join').style.display = 'block'; document.getElementById('room').style.display = 'none'; ws.close(); } function receiveVideo(sender) { var participant = new Participant(sender); participants[sender] = participant; var video = participant.getVideoElement(); var options = { remoteVideo: video, onicecandidate: participant.onIceCandidate.bind(participant) } participant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function (error) { if(error) { return console.error(error); } this.generateOffer (participant.offerToReceiveVideo.bind(participant)); });; } function onParticipantLeft(request) { console.log('Participant ' + request.name + ' left'); var participant = participants[request.name]; participant.dispose(); delete participants[request.name]; } function sendMessage(message) { var jsonMessage = JSON.stringify(message); console.log('Sending message: ' + jsonMessage); ws.send(jsonMessage); } 这里主要依赖于kurento-utils来完成Android上面是官方的代码分析,接下来主要用Android来实现客户端功能,跟one2one一样,都是引用org.webrtc: implementation 'org.webrtc:google-webrtc:1.0.32006' implementation 'org.java-websocket:Java-WebSocket:1.5.3' implementation "com.google.code.gson:gson:2.+"初始化WebSocket,在onMessage里监听JSON信令消息,实现五个方法:existingParticipants,newParticipantArrived,participantLeft,receiveVideoAnswer和iceCandidate when (id) { "existingParticipants" -> { onExistingParticipants(jsonObject) } "newParticipantArrived" -> { onNewParticipantArrived(jsonObject) } "participantLeft" -> { onParticipantLeft(jsonObject) } "receiveVideoAnswer" -> { onReceiveVideoAnswer(jsonObject) } "iceCandidate" -> { iceCandidate(jsonObject) } }existingParticipants:在加入房间后会收到existingParticipants,并告诉有哪些参于者newParticipantArrived:如果有新的参于者加入时,会收到此信息participantLeft:有参于者离开,会收到此信息receiveVideoAnswer:接收到服务器发送的Answer信令iceCandidate:candidates事件发送给服务端的请求:joinRoom:加入到房间的请求receiveVideoFrom:发起接收某参于者时间的请求leaveRoom:离开房间的请求onIceCandidate:将candidate发送到服务端手机与电脑完成连接
2022年11月17日
235 阅读
0 评论
0 点赞
2022-11-11
Android Webrtc之Kurento-one2many-call
Kurento-one2many-call这是官方的一个例子,一个发送视频,N个人来观看,预览者可以随时离开通讯,当发布者完成会话时,每个连接的预览者都会收到一个停止的消息并终止其会话,类似直播。cd kurento-tutorial-java/kurento-one2many-call mvn -U clean spring-boot:run \ -Dspring-boot.run.jvmArguments="-Dkms.url=ws://{KMS_HOST}:8888/kurento"功能说明:发布者创建offer,并设置本地描述;发布者将offer发送presenter到服务端;发布者接收到presenterResponse,获取到answer并设置远程描述;预览者创建offer,并设置本地描述;预览者将offer,发送viewer到服务端;预览者收到viewerResponse,获取到answer并设置远程描述;一对N端的视频完成,预览者可以随时离开当发布者完成会话时,每个连接的预览者都会收到一个stopCommunication消息并终止其会话。以下是官方流程图:发布者://创建Offer val mediaConstraints = MediaConstraints() peerConnection.createOffer(this, mediaConstraints) ... //设置本地描述 override fun onCreateSuccess(sd: SessionDescription?) { peerConnection.setLocalDescription(this, sd) } ... //设置本地描述成功后发送到服务端 override fun onSetSuccess() { peerConnection.localDescription?.let { sendPresenter(it.description) } }接收到presenterResponse后将answer设置为远程描述:if (message["response"].asString == "accepted") { val sdpAnswer = message["sdpAnswer"].asString peerConnection.setRemoteDescription( this, SessionDescription(SessionDescription.Type.ANSWER, sdpAnswer) ) }预览者//创建Offer val mediaConstraints = MediaConstraints() peerConnection.createOffer(this, mediaConstraints) ... //设置本地描述 override fun onCreateSuccess(sd: SessionDescription?) { peerConnection.setLocalDescription(this, sd) } //设置本地描述成功后发送到服务端 override fun onSetSuccess() { peerConnection.localDescription?.let { sendViewer(it.description) } }预览者接收到viewerResponse后将answer设置为远程描述: if (message["response"].asString == "accepted") { val sdpAnswer = message["sdpAnswer"].asString peerConnection!!.setRemoteDescription( this, SessionDescription(SessionDescription.Type.ANSWER, sdpAnswer) ) }手机端发起视频:电脑端查看视频:
2022年11月11日
378 阅读
0 评论
0 点赞
1
2
...
6