Skip to content

add a system_server API for listening to GosPackageState changes; fix racy dispatch of GosPackageState change callback#406

Open
muhomorr wants to merge 10 commits into
GrapheneOS:17from
muhomorr:17_07-10_base
Open

add a system_server API for listening to GosPackageState changes; fix racy dispatch of GosPackageState change callback#406
muhomorr wants to merge 10 commits into
GrapheneOS:17from
muhomorr:17_07-10_base

Conversation

@muhomorr

Copy link
Copy Markdown
Member

No description provided.

Comment on lines +163 to +169
changeCallbacksThread.getThreadHandler().post(() -> {
synchronized (changeCallbacks) {
for (GosPackageStateChangeCallback callback : changeCallbacks) {
callback.onGosPackageStateChanged(uid, updatedGosPs, userId);
}
}
});

@inthewaves inthewaves Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically not safe from callbacks that remove themselves

e.g. see

// @GuardedBy("mLock") - hold lock for writes, no lock necessary for simple reads
final CopyOnWriteArrayList<UserVisibilityListener> mListeners =
new CopyOnWriteArrayList<>();
for existing listener patterns in AOSP (although I'm not convinced they're using mListeners correctly in dispatchVisibilityChanged, as it doesn't use the iterator and thus avoids the COWIterator's snapshots and doesn't even use the listener snapshot passed into the function)

Also what's our policy here for uncaught (runtime) exceptions from a callback? Currently it seems like it'll just crash

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7163a0b

Also what's our policy here for uncaught (runtime) exceptions from a callback?

This is an internal system_server API, I think we should rely on the caller for exception handling.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also 8ca4439

Comment thread services/core/java/com/android/server/pm/GosPackageStatePmHooks.java Outdated
Comment on lines 2519 to 2524
@Override
public void onGosPackageStateChanged(GosPackageState state) {
// this is a oneway method, caller (ActivityManager) will not be blocked
ActivityThreadHooks.onGosPackageStateChanged(mInitialApplication, state, false);
public void onGosPackageStateChanged() {
Context ctx = mInitialApplication;
GosPackageState state = GosPackageState.getForSelf(ctx);
ActivityThreadHooks.onGosPackageStateChanged(ctx, state, false);
}

@inthewaves inthewaves Jul 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a window where mInitialApplication can be null, e.g. if an app overrides Application#attachBaseContext and takes too long, and then storage/contact scopes is enabled during that time.

There seems to already be AOSP code here that accounts for this, e.g. see how mUpdateHttpProxyOnBind is used in ActivityThread#updateHttpProxy, etc.

e.g. as a (contrived) NPE example, consider an app that has this Application:

class SlowStartApplication : Application() {
    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)

        Log.i(TAG, "attachBaseContext: blocking startup for ${STARTUP_BLOCK_MS}ms")
        Log.i(TAG, "attachBaseContext: enable Storage Scopes or Contact Scopes while this process is blocked")
        SystemClock.sleep(STARTUP_BLOCK_MS)
        Log.i(TAG, "attachBaseContext: startup block finished")
    }

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate: application startup completed")
    }

    companion object {
        const val TAG = "SampleApp"
        private const val STARTUP_BLOCK_MS = 12_000L
    }
}

Then attachBaseContext's blocking/slow code can be triggered by the app receiving a broadcast, so it's not always from a user launching the app then going to configure storage/contact scopes. The app can receive a broadcast and run its application code while the user is turning on storage/contact scopes

@inthewaves inthewaves Jul 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NPE when such an app receives broadcast to run its slow attachBaseContext code and Storage Scopes is enabled while it's blocked:

 6933-6933  SampleApp                I  attachBaseContext: blocking startup for 12000ms
 6933-6933  SampleApp                I  attachBaseContext: enable Storage Scopes or Contact Scopes while this process is blocked
 6933-6947  Binder                   W  Caught a RuntimeException from the binder stub implementation.
                                        java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
                                        	at android.content.pm.GosPackageState.getForSelf(GosPackageState.java:244)
                                        	at android.app.ActivityThread$ApplicationThread.onGosPackageStateChanged(ActivityThread.java:2522)
                                        	at android.app.IApplicationThread$Stub.onTransact(IApplicationThread.java:1595)
                                        	at android.app.ActivityThread$ApplicationThread.onTransact(ActivityThread.java:1248)
                                        	at android.os.Binder.execTransactInternal(Binder.java:1417)
                                        	at android.os.Binder.execTransact(Binder.java:1364)
 6933-6933  SampleApp                I  attachBaseContext: startup block finished
 6933-6933  SampleApp                I  onCreate: application startup completed

NPE can also be triggered in the old code with Contact Scopes since that uses the ctx

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

/**
* Called after each successful GosPackageState update on a separate callbacks thread.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale javadoc since it now uses handler

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants