Anas Ambri
Android Montreal group - September 2016
Android & iOS freelancer
@AnasAmbri
Slides @ slides.com/anasambri/first-second
Taken from [1]
Can we create a timeline for what happens when we click on app icon?
Taken from [2]
Will this be useful for you tomorrow?
Probably not
Will it help you make better decisions ?
Definitely
If you have a question, at any point, stop me.
Driver converts touches into a stream of events
Exposes them in a file (/dev/input/eventX)
A class that buffers all events
1 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
//...
3 RawEvent* event = buffer;
4 while (mPendingEventIndex < mPendingEventCount) {
5 Device* device = mDevices.valueAt(deviceIndex);
//...
7 if (eventItem.events & EPOLLIN) {
8 int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
//...
10 size_t count = size_t(readSize) / sizeof(struct input_event);
11 for (size_t i = 0; i < count; i++) {
12 struct input_event& iev = readBuffer[i];
13 event->deviceId = deviceId;
14 event->type = iev.type;
15 event->code = iev.code;
16 event->value = iev.value;
17 event += 1;
18 }
19 }
20 }
21 }
22 }
A class that notifies upper layers of change
1 void InputReader::loopOnce() {
2 Vector<InputDeviceInfo> inputDevices;
3 size_t count = mEventHub->getEvents(
timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
5 { //...
6 if (count) {
7 processEventsLocked(mEventBuffer, count);
8 }
//...
10 // Send out a message that the describes the changed input devices.
11 if (inputDevicesChanged) {
12 mPolicy->notifyInputDevicesChanged(inputDevices);
13 }
14 }
GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz,
"notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V");
private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
//...
mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
mInputDevices).sendToTarget();
}
private final class InputManagerHandler extends Handler {
//...
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DELIVER_INPUT_DEVICES_CHANGED:
deliverInputDevicesChanged((InputDevice[])msg.obj);
break;
}
//...
}
}
A view has a ViewRootImpl
A ViewRootImpl has a WindowInputEventReceiver
WindowInputEventReceiver extends InputEventReceiver
InputEventReceiver has two methods
- handleEvent()
- consumeEvents()
WindowManagerService keeps an eye on all InputEventReceivers
1 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
//...
2 JNIEnv* env = AndroidRuntime::getJNIEnv();
3 status_t status = consumeEvents(env, false /*consumeBatches*/, -1);
4 }
6 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
7 bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
9 for (;;) {
10 InputEvent* inputEvent;
11 status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
//...
13 inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
//...
15 env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
16 }
17 }
1 public abstract class InputEventReceiver {
2 private void dispatchInputEvent(int seq, InputEvent event) {
3 mSeqMap.put(event.getSequenceNumber(), seq);
4 onInputEvent(event);
5 }
6 }
7 final class WindowInputEventReceiver extends InputEventReceiver {
8 public void onInputEvent(InputEvent event) {
9 enqueueInputEvent(event, this, 0, true);
10 }
11 }
12 public final class ViewRootImpl {
13 void enqueueInputEvent(InputEvent event,
14 InputEventReceiver receiver, int flags, boolean processImmediately) {
//...
16 if (processImmediately) {
17 doProcessInputEvents();
18 } else {
19 scheduleProcessInputEvents();
20 }
21 }
22 }
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
}
Drivers send events
Events are buffered, then forwarded to the WindowManagerService
Each view has an InputEventReceiver
InputEventReceiver receive events from the WindowManagerService
A launcher is just an app
<!-- This is the home screen -->
<activity
android:name=".activity.HomeActivity" android:label="Fake Launcher"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
android:launchMode="singleTask" android:stateNotNeeded="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- This is needed, or else your app won't compile -->
<activity
android:name=".activity.LaunchActivity"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
1 //Retrieve all apps on the phone
2 Intent i = new Intent(Intent.ACTION_MAIN, null);
3 i.addCategory(Intent.CATEGORY_LAUNCHER);
5 List<ResolveInfo> availableActivities = packageManager.queryIntentActivities(i, 0);
6 for(ResolveInfo ri:availableActivities){
7 AppDetails details = new AppDetails(
8 ri.activityInfo.loadLabel(packageManager).toString(),
9 ri.activityInfo.packageName,
10 ri.activityInfo.loadIcon(packageManager)
11 );
12 }
14 //Start App
15 Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(appDetails.getPackageName());
16 if (intent != null) {
17 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
18 context.startActivity(intent);
19 }
Like WindowManagerService, but for activities
ResolveInfo rInfo = ActivityThread.getPackageManager().resolveIntent(
intent, resolvedType, PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
This will submit a request to start the Activity (after checking permissions)
Eventually, we create a process
// Is this activity's application already running?
1 ProcessRecord app = getProcessRecordLocked(r.processName, r.info.appInfo.uid);
2 if (app != null && app.thread != null) {
3 try {
4 realStartActivityLocked(r, app, andResume, checkConfig);
5 return;
6 } catch (RemoteException e) { }
7 }
9 startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false);
10 int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
Eventually, we create a process
1 try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, zygoteArgs);
2 } catch (ZygoteStartFailedEx ex) {
3 throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
4 }
5 private static ProcessStartResult startViaZygote(final String processClass,
//...) throws ZygoteStartFailedEx {
6 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
This is a process that gets copied for every app
pid = Zygote.fork();
A process is a wrapper around a task
By forking, the OS saves resources
[1]: http://archive.ncsa.illinois.edu/Cyberia/Cosmos/CosmicMysteryTour.html
[2]: http://www2.r3gis.fr/CSSPresentation/#/8
Would love to hear your feedback @AnasAmbri