| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.bedstead.remoteframeworkclasses.processor; |
| |
| import com.android.bedstead.remoteframeworkclasses.processor.annotations.RemoteFrameworkClasses; |
| |
| import com.google.android.enterprise.connectedapps.annotations.CrossUser; |
| import com.google.auto.service.AutoService; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.io.Resources; |
| import com.squareup.javapoet.AnnotationSpec; |
| import com.squareup.javapoet.ClassName; |
| import com.squareup.javapoet.JavaFile; |
| import com.squareup.javapoet.MethodSpec; |
| import com.squareup.javapoet.ParameterSpec; |
| import com.squareup.javapoet.TypeSpec; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.net.URL; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import javax.annotation.processing.AbstractProcessor; |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.Elements; |
| import javax.tools.JavaFileObject; |
| |
| /** |
| * Processor for generating {@code RemoteSystemService} classes. |
| * |
| * <p>This is started by including the {@link RemoteFrameworkClasses} annotation. |
| * |
| * <p>For each entry in {@code FRAMEWORK_CLASSES} this will generate an interface including all public |
| * and test APIs with the {@code CrossUser} annotation. This interface will be named the same as the |
| * framework class except with a prefix of "Remote", and will be in the same package. |
| * |
| * <p>This will also generate an implementation of the interface which takes an instance of the |
| * framework class in the constructor, and each method proxying calls to the framework class. |
| */ |
| @SupportedAnnotationTypes({ |
| "com.android.bedstead.remoteframeworkclasses.processor.annotations.RemoteFrameworkClasses", |
| }) |
| @AutoService(javax.annotation.processing.Processor.class) |
| public final class Processor extends AbstractProcessor { |
| |
| private static final String[] FRAMEWORK_CLASSES = { |
| "android.app.admin.DevicePolicyManager", |
| "android.net.wifi.WifiManager", |
| "android.os.HardwarePropertiesManager", |
| "android.os.UserManager", |
| "android.content.pm.PackageManager", |
| "android.content.pm.CrossProfileApps", |
| "android.content.pm.LauncherApps", |
| "android.accounts.AccountManager", |
| "android.app.Activity", |
| "android.content.Context", |
| "android.content.ContentResolver", |
| "android.security.KeyChain" |
| }; |
| |
| private static final String PARENT_PROFILE_INSTANCE = |
| "public android.app.admin.DevicePolicyManager getParentProfileInstance(android" |
| + ".content.ComponentName)"; |
| private static final String GET_CONTENT_RESOLVER = |
| "public android.content.ContentResolver getContentResolver()"; |
| |
| private static final Set<String> BLOCKLISTED_METHODS = ImmutableSet.of( |
| // DevicePolicyManager |
| |
| // Uses ServiceConnection |
| "public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android" |
| + ".content.Intent, android.content.ServiceConnection, int, android.os" |
| + ".UserHandle)", |
| // Uses AttestedKeyPair |
| "public android.security.AttestedKeyPair generateKeyPair(android.content" |
| + ".ComponentName, String, android.security.keystore.KeyGenParameterSpec, int)", |
| // Uses Executor |
| "public void installSystemUpdate(@NonNull android.content.ComponentName, android.net" |
| + ".Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager" |
| + ".InstallSystemUpdateCallback)", |
| |
| // WifiManager |
| |
| // Uses Executor |
| "public void addSuggestionConnectionStatusListener(java.util.concurrent.Executor, " |
| + "android.net.wifi.WifiManager.SuggestionConnectionStatusListener)", |
| "public void addSuggestionUserApprovalStatusListener(java.util.concurrent.Executor, " |
| + "android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener)", |
| "public void clearApplicationUserData(android.content.ComponentName, @NonNull String," |
| + " @NonNull java.util.concurrent.Executor, android.app.admin" |
| + ".DevicePolicyManager.OnClearApplicationUserDataListener)", |
| "public void registerScanResultsCallback(java.util.concurrent.Executor, android.net" |
| + ".wifi.WifiManager.ScanResultsCallback)", |
| "public void registerSubsystemRestartTrackingCallback(java.util.concurrent.Executor, " |
| + "android.net.wifi.WifiManager.SubsystemRestartTrackingCallback)", |
| // Uses WpsCallback |
| "public void cancelWps(android.net.wifi.WifiManager.WpsCallback)", |
| // Uses MulticastLock |
| "public android.net.wifi.WifiManager.MulticastLock createMulticastLock(String)", |
| // Uses WifiLock |
| "public android.net.wifi.WifiManager.WifiLock createWifiLock(int, String)", |
| "public android.net.wifi.WifiManager.WifiLock createWifiLock(String)", |
| // Uses SuggestionConnectionStatusListener |
| "public void removeSuggestionConnectionStatusListener(android.net.wifi.WifiManager" |
| + ".SuggestionConnectionStatusListener)", |
| // Uses SuggestionUserApprovalStatusListener |
| "public void removeSuggestionUserApprovalStatusListener(android.net.wifi.WifiManager" |
| + ".SuggestionUserApprovalStatusListener)", |
| // Uses LocalOnlyHotspotCallback |
| "public void startLocalOnlyHotspot(android.net.wifi.WifiManager" |
| + ".LocalOnlyHotspotCallback, android.os.Handler)", |
| // Uses WpsCallback |
| "public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager" |
| + ".WpsCallback)", |
| // Uses ScanResultsCallback |
| "public void unregisterScanResultsCallback(@NonNull android.net.wifi.WifiManager" |
| + ".ScanResultsCallback)", |
| // Uses SubsystemRestartTrackingCallback |
| "public void unregisterSubsystemRestartTrackingCallback(android.net.wifi.WifiManager" |
| + ".SubsystemRestartTrackingCallback)", |
| |
| // PackageManager |
| |
| // Uses IBinder |
| "public android.os.IBinder getHoldLockToken()", |
| "public void holdLock(android.os.IBinder, int)", |
| // Uses Drawable |
| "public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull " |
| + "android.content.ComponentName) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull " |
| + "android.content.Intent) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android" |
| + ".content.ComponentName) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android" |
| + ".content.Intent) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android" |
| + ".content.ComponentName) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android" |
| + ".content.Intent) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getApplicationBanner(@NonNull " |
| + "android.content.pm.ApplicationInfo)", |
| "public abstract android.graphics.drawable.Drawable getApplicationBanner(@NonNull " |
| + "String) throws android.content.pm.PackageManager.NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull " |
| + "android.content.pm.ApplicationInfo)", |
| "public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull " |
| + "String) throws android.content.pm.PackageManager.NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull " |
| + "android.content.pm.ApplicationInfo)", |
| "public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull " |
| + "String) throws android.content.pm.PackageManager.NameNotFoundException", |
| "public abstract android.graphics.drawable.Drawable getDefaultActivityIcon()", |
| "public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, " |
| + "@DrawableRes int, @Nullable android.content.pm.ApplicationInfo)", |
| "public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity" |
| + "(@NonNull android.graphics.drawable.Drawable, @NonNull android.os" |
| + ".UserHandle, @Nullable android.graphics.Rect, int)", |
| "public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull " |
| + "android.graphics.drawable.Drawable, @NonNull android.os.UserHandle)", |
| "public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable)", |
| // Uses Executor |
| "public void getGroupOfPlatformPermission(@NonNull String, @NonNull java.util" |
| + ".concurrent.Executor, @NonNull java.util.function.Consumer<java.lang" |
| + ".String>)", |
| "public void getPlatformPermissionsForGroup(@NonNull String, @NonNull java.util" |
| + ".concurrent.Executor, @NonNull java.util.function.Consumer<java.util" |
| + ".List<java.lang.String>>)", |
| // Uses Resources |
| "public abstract android.content.res.Resources getResourcesForActivity(@NonNull " |
| + "android.content.ComponentName) throws android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| "public abstract android.content.res.Resources getResourcesForApplication(@NonNull " |
| + "android.content.pm.ApplicationInfo) throws android.content.pm" |
| + ".PackageManager.NameNotFoundException", |
| "public android.content.res.Resources getResourcesForApplication(@NonNull android" |
| + ".content.pm.ApplicationInfo, @Nullable android.content.res.Configuration) " |
| + "throws android.content.pm.PackageManager.NameNotFoundException", |
| "public abstract android.content.res.Resources getResourcesForApplication(@NonNull " |
| + "String) throws android.content.pm.PackageManager.NameNotFoundException", |
| // Uses PackageInstaller |
| "public abstract android.content.pm.PackageInstaller getPackageInstaller()", |
| // Uses XmlResourceParser |
| "public abstract android.content.res.XmlResourceParser getXml(@NonNull String, " |
| + "@XmlRes int, @Nullable android.content.pm.ApplicationInfo)", |
| // Uses OnChecksumsReadyListener |
| "public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util" |
| + ".List<java.security.cert.Certificate>, @NonNull android.content.pm" |
| + ".PackageManager.OnChecksumsReadyListener) throws java.security.cert" |
| + ".CertificateEncodingException, android.content.pm.PackageManager" |
| + ".NameNotFoundException", |
| |
| // CrossProfileApps |
| |
| // Uses Drawable |
| "public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(" |
| + "android.os.UserHandle)", |
| // Uses Activity |
| "public void startActivity(" |
| + "android.content.Intent, android.os.UserHandle, android.app.Activity)", |
| "public void startActivity(android.content.Intent, android.os.UserHandle," |
| + "android.app.Activity, android.os.Bundle)", |
| |
| // LauncherApps |
| |
| //Uses LauncherApps.Callback |
| "public void registerCallback(" |
| + "android.content.pm.LauncherApps.Callback, android.os.Handler)", |
| "public void registerCallback(" |
| + "android.content.pm.LauncherApps.Callback)", |
| |
| //Uses Drawable |
| "public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(" |
| + "android.content.pm.ShortcutInfo, int)", |
| "public android.graphics.drawable.Drawable getShortcutIconDrawable(" |
| + "android.content.pm.ShortcutInfo, int)", |
| |
| //Uses Executor |
| "public void registerPackageInstallerSessionCallback(" |
| + "@NonNull @CallbackExecutor java.util.concurrent.Executor," |
| + "@NonNull android.content.pm.PackageInstaller.SessionCallback)", |
| |
| // Uses LauncherActivityInfo |
| "public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(" |
| + "String, android.os.UserHandle)", |
| "public java.util.List<android.content.pm.LauncherActivityInfo> " |
| + "getShortcutConfigActivityList(String, android.os.UserHandle)", |
| "public android.content.IntentSender getShortcutConfigActivityIntent(" |
| + "@NonNull android.content.pm.LauncherActivityInfo)", |
| "public android.content.pm.LauncherActivityInfo resolveActivity(" |
| + "android.content.Intent, android.os.UserHandle)", |
| |
| //Uses LauncherApps.ShortcutQuery |
| "public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(" |
| + "android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle)", |
| "public void unregisterCallback(android.content.pm.LauncherApps.Callback)", |
| |
| //Uses PackageInfo.SessionCallback |
| "public void unregisterPackageInstallerSessionCallback(" |
| + "android.content.pm.PackageInstaller.SessionCallback)", |
| |
| // AccountManager |
| |
| // Uses Activity |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| |
| // Uses OnAccountsUpdateListener |
| "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean)", |
| "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[])", |
| "public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener)", |
| |
| // Uses AccountManagerCallback |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)", |
| "public android.os.Bundle hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)", |
| "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)", |
| "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, String) throws android.accounts.NetworkErrorException", |
| "public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)", |
| "public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler)", |
| |
| // Uses android.accounts.AccountManager |
| "public static android.accounts.AccountManager get(android.content.Context)", |
| |
| // Activity |
| |
| // Uses android.view.View |
| "public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams)", |
| "@Nullable public android.view.View getCurrentFocus()", |
| "@Nullable public android.view.View onCreatePanelView(int)", |
| "@Nullable public android.view.View onCreateView(@NonNull String, @NonNull android.content.Context, @NonNull android.util.AttributeSet)", |
| "@Nullable public android.view.View onCreateView(@Nullable android.view.View, @NonNull String, @NonNull android.content.Context, @NonNull android.util.AttributeSet)", |
| "public boolean onPreparePanel(int, @Nullable android.view.View, @NonNull android.view.Menu)", |
| "public void openContextMenu(android.view.View)", |
| "public void registerForContextMenu(android.view.View)", |
| "public void setContentView(android.view.View)", |
| "public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams)", |
| "public void unregisterForContextMenu(android.view.View)", |
| |
| // Uses java.io.FileDescriptor |
| "public void dump(@NonNull String, @Nullable java.io.FileDescriptor, @NonNull java.io.PrintWriter, @Nullable String[])", |
| |
| // Uses android.app.Activity |
| "@Deprecated public void finishActivityFromChild(@NonNull android.app.Activity, int)", |
| "@Deprecated public void finishFromChild(android.app.Activity)", |
| "public final android.app.Activity getParent()", |
| "@Deprecated public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent)", |
| "protected void onChildTitleChanged(android.app.Activity, CharSequence)", |
| "@Deprecated public boolean onNavigateUpFromChild(android.app.Activity)", |
| "@Deprecated public void startActivityFromChild(@NonNull android.app.Activity, @RequiresPermission android.content.Intent, int)", |
| "@Deprecated public void startActivityFromChild(@NonNull android.app.Activity, @RequiresPermission android.content.Intent, int, @Nullable android.os.Bundle)", |
| "@Deprecated public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException", |
| "@Deprecated public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, @Nullable android.os.Bundle) throws android.content.IntentSender.SendIntentException", |
| |
| // Uses android.app.ActionBar |
| "@Nullable public android.app.ActionBar getActionBar()", |
| |
| // Uses android.app.Application |
| "public final android.app.Application getApplication()", |
| |
| // Uses android.app.Fragment |
| "@Deprecated public void onAttachFragment(android.app.Fragment)", |
| "@Deprecated public void startActivityFromFragment(@NonNull android.app.Fragment, @RequiresPermission android.content.Intent, int)", |
| "@Deprecated public void startActivityFromFragment(@NonNull android.app.Fragment, @RequiresPermission android.content.Intent, int, @Nullable android.os.Bundle)", |
| |
| // Uses android.app.FragmentManager |
| "@Deprecated public android.app.FragmentManager getFragmentManager()", |
| |
| // Uses android.transition.Scene |
| "public android.transition.Scene getContentScene()", |
| |
| // Uses android.transition.TransitionManager |
| "public android.transition.TransitionManager getContentTransitionManager()", |
| "public void setContentTransitionManager(android.transition.TransitionManager)", |
| |
| // Uses Object |
| "@Nullable public Object getLastNonConfigurationInstance()", |
| "public Object onRetainNonConfigurationInstance()", |
| |
| // Uses android.view.LayoutInflater |
| "@NonNull public android.view.LayoutInflater getLayoutInflater()", |
| |
| // Uses android.view.MenuInflater |
| "@NonNull public android.view.MenuInflater getMenuInflater()", |
| |
| // Uses android.app.LoaderManager |
| "@Deprecated public android.app.LoaderManager getLoaderManager()", |
| |
| // Uses android.media.session.MediaController |
| "public final android.media.session.MediaController getMediaController()", |
| |
| // Uses android.content.SharedPreferences |
| "public android.content.SharedPreferences getPreferences(int)", |
| |
| // Uses android.view.SearchEvent |
| "public final android.view.SearchEvent getSearchEvent()", |
| |
| // Uses android.window.SplashScreen |
| "@NonNull public final android.window.SplashScreen getSplashScreen()", |
| |
| // Uses android.app.VoiceInteractor |
| "public android.app.VoiceInteractor getVoiceInteractor()", |
| |
| // Uses android.view.Window |
| "public android.view.Window getWindow()", |
| |
| // Uses android.view.WindowManager |
| "public android.view.WindowManager getWindowManager()", |
| |
| // Uses android.database.Cursor |
| "@Deprecated public final android.database.Cursor managedQuery(android.net.Uri, String[], String, String[], String)", |
| "@Deprecated public void startManagingCursor(android.database.Cursor)", |
| "@Deprecated public void stopManagingCursor(android.database.Cursor)", |
| |
| // Uses android.view.ActionMode |
| "@CallSuper public void onActionModeFinished(android.view.ActionMode)", |
| "@CallSuper public void onActionModeStarted(android.view.ActionMode)", |
| "@Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback)", |
| "@Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int)", |
| "@Nullable public android.view.ActionMode startActionMode(android.view.ActionMode.Callback)", |
| "@Nullable public android.view.ActionMode startActionMode(android.view.ActionMode.Callback, int)", |
| |
| // Uses android.view.MenuItem |
| "public boolean onContextItemSelected(@NonNull android.view.MenuItem)", |
| "public void onContextMenuClosed(@NonNull android.view.Menu)", |
| "public boolean onMenuItemSelected(int, @NonNull android.view.MenuItem)", |
| "public boolean onOptionsItemSelected(@NonNull android.view.MenuItem)", |
| |
| // Uses android.view.ContextMenu |
| "public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)", |
| |
| // Uses android.app.Dialog |
| "@Deprecated protected android.app.Dialog onCreateDialog(int)", |
| "@Deprecated @Nullable protected android.app.Dialog onCreateDialog(int, android.os.Bundle)", |
| "@Deprecated protected void onPrepareDialog(int, android.app.Dialog)", |
| "@Deprecated protected void onPrepareDialog(int, android.app.Dialog, android.os.Bundle)", |
| |
| // Uses android.app.TaskStackBuilder |
| "public void onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)", |
| "public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)", |
| |
| // Uses android.view.Menu |
| "public boolean onCreateOptionsMenu(android.view.Menu)", |
| "public boolean onCreatePanelMenu(int, @NonNull android.view.Menu)", |
| "public boolean onMenuOpened(int, @NonNull android.view.Menu)", |
| "public void onOptionsMenuClosed(android.view.Menu)", |
| "public void onPanelClosed(int, @NonNull android.view.Menu)", |
| "public boolean onPrepareOptionsMenu(android.view.Menu)", |
| |
| // Uses android.graphics.Canvas |
| "@Deprecated public boolean onCreateThumbnail(android.graphics.Bitmap, android.graphics.Canvas)", |
| |
| // Uses android.os.CancellationSignal |
| "public void onGetDirectActions(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>)", |
| "public void onPerformDirectAction(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>)", |
| |
| // Uses android.view.SearchEvent |
| "public boolean onSearchRequested(@Nullable android.view.SearchEvent)", |
| |
| // Uses android.app.Application.ActivityLifecycleCallbacks |
| "public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks)", |
| "public void unregisterActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks)", |
| |
| // Uses Runnable |
| "public final void runOnUiThread(Runnable)", |
| |
| // Uses android.widget.Toolbar |
| "public void setActionBar(@Nullable android.widget.Toolbar)", |
| |
| // Uses android.app.SharedElementCallback |
| "public void setEnterSharedElementCallback(android.app.SharedElementCallback)", |
| "public void setExitSharedElementCallback(android.app.SharedElementCallback)", |
| |
| // Uses Drawable |
| "public final void setFeatureDrawable(int, android.graphics.drawable.Drawable)", |
| |
| // Uses android.media.session.MediaController |
| "public final void setMediaController(android.media.session.MediaController)", |
| |
| // Context |
| |
| // Uses Object |
| "public abstract Object getSystemService(@NonNull String)", |
| |
| // ContextThemeWrapper |
| "protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean)", |
| |
| // Context |
| |
| // Uses java.util.concurrent.Executor |
| "public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection)", |
| "public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection)", |
| |
| // Uses android.content.ServiceConnection |
| "public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int)", |
| "public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle)", |
| "public abstract void unbindService(@NonNull android.content.ServiceConnection)", |
| "public void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int)", |
| |
| // Uses android.content.Context |
| "@NonNull public android.content.Context createAttributionContext(@Nullable String)", |
| "public abstract android.content.Context createConfigurationContext(@NonNull android.content.res.Configuration)", |
| "@NonNull public android.content.Context createContext(@NonNull android.content.ContextParams)", |
| "public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException", |
| "public abstract android.content.Context createDeviceProtectedStorageContext()", |
| "@DisplayContext public abstract android.content.Context createDisplayContext(@NonNull android.view.Display)", |
| "public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException", |
| "@NonNull @UiContext public android.content.Context createWindowContext(int, @Nullable android.os.Bundle)", |
| "@NonNull @UiContext public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle)", |
| "public abstract android.content.Context getApplicationContext()", |
| |
| // Uses android.content.res.AssetManager |
| "public abstract android.content.res.AssetManager getAssets()", |
| |
| // Uses java.lang.ClassLoader |
| "public abstract ClassLoader getClassLoader()", |
| |
| // Uses android.view.Display |
| "@Nullable public android.view.Display getDisplay()", |
| |
| // Uses android.graphics.drawable.Drawable |
| "@Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int)", |
| "@Deprecated public abstract android.graphics.drawable.Drawable getWallpaper()", |
| "@Deprecated public abstract android.graphics.drawable.Drawable peekWallpaper()", |
| |
| // Uses java.util.concurrent.Executor |
| "public java.util.concurrent.Executor getMainExecutor()", |
| |
| // Uses android.os.Looper |
| "public abstract android.os.Looper getMainLooper()", |
| |
| // Uses android.content.pm.PackageManager |
| "public abstract android.content.pm.PackageManager getPackageManager()", |
| |
| // Uses android.content.ContextParams |
| "@Nullable public android.content.ContextParams getParams()", |
| |
| // Uses android.content.res.Resources |
| "public abstract android.content.res.Resources getResources()", |
| |
| // Uses android.content.SharedPreferences |
| "public abstract android.content.SharedPreferences getSharedPreferences(String, int)", |
| |
| // Uses android.content.res.Resources.Theme |
| "public abstract android.content.res.Resources.Theme getTheme()", |
| |
| // Uses android.content.res.TypedArray |
| "@NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[])", |
| "@NonNull public final android.content.res.TypedArray obtainStyledAttributes(@StyleRes int, @NonNull @StyleableRes int[]) throws android.content.res.Resources.NotFoundException", |
| "@NonNull public final android.content.res.TypedArray obtainStyledAttributes(@Nullable android.util.AttributeSet, @NonNull @StyleableRes int[])", |
| "@NonNull public final android.content.res.TypedArray obtainStyledAttributes(@Nullable android.util.AttributeSet, @NonNull @StyleableRes int[], @AttrRes int, @StyleRes int)", |
| |
| // Uses java.io.FileInputStream |
| "public abstract java.io.FileInputStream openFileInput(String) throws java.io.FileNotFoundException", |
| |
| // Uses java.io.FileOutputStream |
| "public abstract java.io.FileOutputStream openFileOutput(String, int) throws java.io.FileNotFoundException", |
| |
| // Uses android.database.sqlite.SQLiteDatabase |
| "public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory)", |
| "public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler)", |
| |
| // Uses android.content.ComponentCallbacks |
| "public void registerComponentCallbacks(android.content.ComponentCallbacks)", |
| "public void unregisterComponentCallbacks(android.content.ComponentCallbacks)", |
| |
| // Uses android.content.BroadcastReceiver |
| "@Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter)", |
| "@Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int)", |
| "@Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler)", |
| "@Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int)", |
| "public abstract void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle)", |
| "public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle)", |
| "public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle)", |
| "public abstract void sendStickyOrderedBroadcast(@RequiresPermission android.content.Intent, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle)", |
| "public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle)", |
| "public abstract void unregisterReceiver(android.content.BroadcastReceiver)", |
| |
| // Uses java.io.InputStream |
| "@Deprecated public abstract void setWallpaper(java.io.InputStream) throws java.io.IOException", |
| |
| |
| // Doesn't make sense as it requires an actual Context |
| "public abstract boolean moveDatabaseFrom(android.content.Context, String)", |
| "public abstract boolean moveSharedPreferencesFrom(android.content.Context, String)", |
| |
| // ContentResolver |
| |
| // Uses Object |
| "public static Object addStatusChangeListener(int, android.content.SyncStatusObserver)", |
| "public static void removeStatusChangeListener(Object)", |
| |
| // Uses android.content.ContentProviderClient |
| "@Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull android.net.Uri)", |
| "@Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull String)", |
| "@Nullable public final android.content.ContentProviderClient acquireUnstableContentProviderClient(@NonNull android.net.Uri)", |
| "@Nullable public final android.content.ContentProviderClient acquireUnstableContentProviderClient(@NonNull String)", |
| |
| // Uses android.content.ContentResolver.MimeTypeInfo |
| "@NonNull public final android.content.ContentResolver.MimeTypeInfo getTypeInfo(@NonNull String)", |
| |
| // Uses android.util.Size |
| "@NonNull public android.graphics.Bitmap loadThumbnail(@NonNull android.net.Uri, @NonNull android.util.Size, @Nullable android.os.CancellationSignal) throws java.io.IOException", |
| |
| // Uses android.database.ContentObserver |
| "public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver)", |
| "@Deprecated public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, boolean)", |
| "public void notifyChange(@NonNull android.net.Uri, @Nullable android.database.ContentObserver, int)", |
| "public void notifyChange(@NonNull java.util.Collection<android.net.Uri>, @Nullable android.database.ContentObserver, int)", |
| "public final void registerContentObserver(@NonNull android.net.Uri, boolean, @NonNull android.database.ContentObserver)", |
| "public final void unregisterContentObserver(@NonNull android.database.ContentObserver)", |
| |
| // Uses android.os.CancellationSignal |
| "@Nullable public final android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "@Nullable public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "@Nullable public final android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "@Nullable public final android.os.ParcelFileDescriptor openFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "@Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "@Nullable public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException", |
| "public final boolean refresh(@NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal)", |
| |
| // Uses java.io.InputStream |
| "@Nullable public final java.io.InputStream openInputStream(@NonNull android.net.Uri) throws java.io.FileNotFoundException", |
| |
| // Uses java.io.OutputStream |
| "@Nullable public final java.io.OutputStream openOutputStream(@NonNull android.net.Uri) throws java.io.FileNotFoundException", |
| "@Nullable public final java.io.OutputStream openOutputStream(@NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException", |
| |
| // Uses android.database.Cursor |
| "@Nullable public final android.database.Cursor query(@NonNull @RequiresPermission.Read android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String)", |
| "@Nullable public final android.database.Cursor query(@NonNull @RequiresPermission.Read android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal)", |
| "@Nullable public final android.database.Cursor query(@NonNull @RequiresPermission.Read android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal)", |
| |
| // Uses android.content.ContentResolver |
| "@NonNull public static android.content.ContentResolver wrap(@NonNull android.content.ContentProvider)", |
| "@NonNull public static android.content.ContentResolver wrap(@NonNull android.content.ContentProviderClient)", |
| |
| |
| // KeyChain |
| |
| // Uses android.app.Activity |
| "public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable String, int, @Nullable String)", |
| "public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable android.net.Uri, @Nullable String)" |
| |
| |
| |
| |
| ); |
| |
| private static final ClassName NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME = |
| ClassName.get("com.android.bedstead.remoteframeworkclasses", |
| "NullParcelableRemoteDevicePolicyManager"); |
| private static final ClassName NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME = |
| ClassName.get("com.android.bedstead.remoteframeworkclasses", |
| "NullParcelableRemoteContentResolver"); |
| private static final ClassName COMPONENT_NAME_CLASSNAME = |
| ClassName.get("android.content", "ComponentName"); |
| |
| private static final ClassName ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME = |
| ClassName.get( |
| "com.android.bedstead.remoteframeworkclasses", "AccountManagerFutureWrapper"); |
| |
| @Override |
| public SourceVersion getSupportedSourceVersion() { |
| return SourceVersion.latest(); |
| } |
| |
| @Override |
| public boolean process(Set<? extends TypeElement> annotations, |
| RoundEnvironment roundEnv) { |
| if (!roundEnv.getElementsAnnotatedWith(RemoteFrameworkClasses.class).isEmpty()) { |
| Set<MethodSignature> blocklistedMethodSignatures = BLOCKLISTED_METHODS.stream() |
| .map(m -> MethodSignature.forApiString( |
| m, processingEnv.getTypeUtils(), processingEnv.getElementUtils())) |
| .collect(Collectors.toSet()); |
| |
| for (String systemService : FRAMEWORK_CLASSES) { |
| TypeElement typeElement = |
| processingEnv.getElementUtils().getTypeElement(systemService); |
| generateRemoteSystemService( |
| typeElement, blocklistedMethodSignatures, processingEnv.getElementUtils()); |
| } |
| |
| generateWrappers(); |
| } |
| |
| return true; |
| } |
| |
| private void generateWrappers() { |
| generateWrapper(NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME); |
| generateWrapper(NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME); |
| } |
| |
| private void generateWrapper(ClassName className) { |
| String contents = null; |
| try { |
| URL url = Processor.class.getResource( |
| "/parcelablewrappers/" + className.simpleName() + ".java.txt"); |
| contents = Resources.toString(url, StandardCharsets.UTF_8); |
| } catch (IOException e) { |
| throw new IllegalStateException("Could not parse wrapper " + className, e); |
| } |
| |
| JavaFileObject builderFile; |
| try { |
| builderFile = processingEnv.getFiler() |
| .createSourceFile(className.packageName() + "." + className.simpleName()); |
| } catch (IOException e) { |
| throw new IllegalStateException( |
| "Could not write parcelablewrapper for " + className, e); |
| } |
| |
| try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { |
| out.write(contents); |
| } catch (IOException e) { |
| throw new IllegalStateException( |
| "Could not write parcelablewrapper for " + className, e); |
| } |
| } |
| |
| private void generateRemoteSystemService( |
| TypeElement frameworkClass, |
| Set<MethodSignature> blocklistedMethodSignatures, |
| Elements elements) { |
| Set<ExecutableElement> methods = filterMethods(getMethods(frameworkClass, |
| processingEnv.getElementUtils()), |
| Apis.forClass(frameworkClass.getQualifiedName().toString(), |
| processingEnv.getTypeUtils(), processingEnv.getElementUtils()), elements) |
| .stream() |
| .filter(t -> !blocklistedMethodSignatures.contains( |
| MethodSignature.forMethod(t, elements))) |
| .collect(Collectors.toSet()); |
| |
| generateFrameworkInterface(frameworkClass, methods); |
| generateFrameworkImpl(frameworkClass, methods); |
| |
| if (frameworkClass.getSimpleName().contentEquals("DevicePolicyManager")) { |
| // Special case, we need to support the .getParentProfileInstance method |
| generateDpmParent(frameworkClass, methods); |
| } |
| } |
| |
| private void generateFrameworkInterface( |
| TypeElement frameworkClass, Set<ExecutableElement> methods) { |
| MethodSignature parentProfileInstanceSignature = |
| MethodSignature.forApiString(PARENT_PROFILE_INSTANCE, processingEnv.getTypeUtils(), |
| processingEnv.getElementUtils()); |
| MethodSignature getContentResolverSignature = |
| MethodSignature.forApiString(GET_CONTENT_RESOLVER, processingEnv.getTypeUtils(), |
| processingEnv.getElementUtils()); |
| |
| Map<MethodSignature, ClassName> signatureReturnOverrides = new HashMap<>(); |
| signatureReturnOverrides.put(parentProfileInstanceSignature, |
| ClassName.get("android.app.admin", "RemoteDevicePolicyManager")); |
| signatureReturnOverrides.put(getContentResolverSignature, |
| ClassName.get("android.content", "RemoteContentResolver")); |
| |
| String packageName = frameworkClass.getEnclosingElement().toString(); |
| ClassName className = ClassName.get(packageName, |
| "Remote" + frameworkClass.getSimpleName().toString()); |
| ClassName implClassName = ClassName.get(packageName, |
| "Remote" + frameworkClass.getSimpleName().toString() + "Impl"); |
| TypeSpec.Builder classBuilder = |
| TypeSpec.interfaceBuilder(className) |
| .addModifiers(Modifier.PUBLIC); |
| |
| classBuilder.addJavadoc("Public, test, and system interface for {@link $T}.\n\n", |
| frameworkClass); |
| classBuilder.addJavadoc("<p>All methods are annotated {@link $T} for compatibility with the" |
| + " Connected Apps SDK.\n\n", CrossUser.class); |
| classBuilder.addJavadoc("<p>For implementation see {@link $T}.\n", implClassName); |
| |
| |
| classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class) |
| .addMember("parcelableWrappers", "{$T.class, $T.class}", |
| NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME, |
| NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME) |
| .addMember("futureWrappers", "$T.class", |
| ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME) |
| .build()); |
| |
| for (ExecutableElement method : methods) { |
| MethodSpec.Builder methodBuilder = |
| MethodSpec.methodBuilder(method.getSimpleName().toString()) |
| .returns(ClassName.get(method.getReturnType())) |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .addAnnotation(CrossUser.class); |
| |
| |
| MethodSignature signature = MethodSignature.forMethod(method, |
| processingEnv.getElementUtils()); |
| |
| if (signatureReturnOverrides.containsKey(signature)) { |
| methodBuilder.returns(signatureReturnOverrides.get(signature)); |
| } |
| |
| methodBuilder.addJavadoc("See {@link $T#$L}.", |
| ClassName.get(frameworkClass.asType()), method.getSimpleName()); |
| |
| for (TypeMirror thrownType : method.getThrownTypes()) { |
| methodBuilder.addException(ClassName.get(thrownType)); |
| } |
| |
| for (VariableElement param : method.getParameters()) { |
| ParameterSpec parameterSpec = |
| ParameterSpec.builder(ClassName.get(param.asType()), |
| param.getSimpleName().toString()).build(); |
| |
| methodBuilder.addParameter(parameterSpec); |
| } |
| |
| classBuilder.addMethod(methodBuilder.build()); |
| } |
| |
| writeClassToFile(packageName, classBuilder.build()); |
| } |
| |
| private void generateDpmParent(TypeElement frameworkClass, Set<ExecutableElement> methods) { |
| MethodSignature parentProfileInstanceSignature = MethodSignature.forApiString( |
| PARENT_PROFILE_INSTANCE, processingEnv.getTypeUtils(), |
| processingEnv.getElementUtils()); |
| String packageName = frameworkClass.getEnclosingElement().toString(); |
| ClassName className = |
| ClassName.get(packageName, "Remote" + frameworkClass.getSimpleName() + "Parent"); |
| TypeSpec.Builder classBuilder = |
| TypeSpec.classBuilder(className).addModifiers(Modifier.FINAL, Modifier.PUBLIC); |
| |
| classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class) |
| .addMember("parcelableWrappers", "{$T.class, $T.class}", |
| NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME, |
| NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME) |
| .build()); |
| |
| classBuilder.addField(ClassName.get(frameworkClass), |
| "mFrameworkClass", Modifier.PRIVATE, Modifier.FINAL); |
| |
| classBuilder.addMethod( |
| MethodSpec.constructorBuilder() |
| .addModifiers(Modifier.PUBLIC) |
| .addParameter(ClassName.get(frameworkClass), "frameworkClass") |
| .addCode("mFrameworkClass = frameworkClass;") |
| .build() |
| ); |
| |
| for (ExecutableElement method : methods) { |
| MethodSpec.Builder methodBuilder = |
| MethodSpec.methodBuilder(method.getSimpleName().toString()) |
| .returns(ClassName.get(method.getReturnType())) |
| .addModifiers(Modifier.PUBLIC) |
| .addAnnotation(CrossUser.class); |
| |
| MethodSignature signature = MethodSignature.forMethod(method, |
| processingEnv.getElementUtils()); |
| |
| for (TypeMirror thrownType : method.getThrownTypes()) { |
| methodBuilder.addException(ClassName.get(thrownType)); |
| } |
| |
| methodBuilder.addParameter(COMPONENT_NAME_CLASSNAME, "profileOwnerComponentName"); |
| |
| List<String> paramNames = new ArrayList<>(); |
| |
| for (VariableElement param : method.getParameters()) { |
| String paramName = param.getSimpleName().toString(); |
| ParameterSpec parameterSpec = |
| ParameterSpec.builder(ClassName.get(param.asType()), paramName).build(); |
| |
| paramNames.add(paramName); |
| |
| methodBuilder.addParameter(parameterSpec); |
| } |
| |
| if (signature.equals(parentProfileInstanceSignature)) { |
| // Special case, we want to return a RemoteDevicePolicyManager instead |
| methodBuilder.returns(ClassName.get( |
| "android.app.admin", "RemoteDevicePolicyManager")); |
| methodBuilder.addStatement( |
| "mFrameworkClass.getParentProfileInstance(profileOwnerComponentName).$L" |
| + "($L)", |
| method.getSimpleName(), String.join(", ", paramNames)); |
| methodBuilder.addStatement("throw new $T($S)", UnsupportedOperationException.class, |
| "TestApp does not support calling .getParentProfileInstance() on a parent" |
| + "."); |
| } else if (method.getReturnType().getKind().equals(TypeKind.VOID)) { |
| methodBuilder.addStatement( |
| "mFrameworkClass.getParentProfileInstance(profileOwnerComponentName).$L" |
| + "($L)", |
| method.getSimpleName(), String.join(", ", paramNames)); |
| } else { |
| methodBuilder.addStatement( |
| "return mFrameworkClass.getParentProfileInstance" |
| + "(profileOwnerComponentName).$L($L)", |
| method.getSimpleName(), String.join(", ", paramNames)); |
| } |
| |
| classBuilder.addMethod(methodBuilder.build()); |
| } |
| |
| writeClassToFile(packageName, classBuilder.build()); |
| } |
| |
| private void generateFrameworkImpl(TypeElement frameworkClass, Set<ExecutableElement> methods) { |
| MethodSignature parentProfileInstanceSignature = |
| MethodSignature.forApiString(PARENT_PROFILE_INSTANCE, processingEnv.getTypeUtils(), |
| processingEnv.getElementUtils()); |
| MethodSignature getContentResolverSignature = |
| MethodSignature.forApiString(GET_CONTENT_RESOLVER, processingEnv.getTypeUtils(), |
| processingEnv.getElementUtils()); |
| |
| Map<MethodSignature, ClassName> signatureReturnOverrides = new HashMap<>(); |
| signatureReturnOverrides.put(parentProfileInstanceSignature, |
| ClassName.get("android.app.admin", "RemoteDevicePolicyManager")); |
| signatureReturnOverrides.put(getContentResolverSignature, |
| ClassName.get("android.content", "RemoteContentResolver")); |
| |
| String packageName = frameworkClass.getEnclosingElement().toString(); |
| ClassName interfaceClassName = ClassName.get(packageName, |
| "Remote" + frameworkClass.getSimpleName().toString()); |
| ClassName className = ClassName.get(packageName, |
| "Remote" + frameworkClass.getSimpleName().toString() + "Impl"); |
| TypeSpec.Builder classBuilder = |
| TypeSpec.classBuilder( |
| className) |
| .addSuperinterface(interfaceClassName) |
| .addModifiers(Modifier.PUBLIC); |
| |
| classBuilder.addField(ClassName.get(frameworkClass), |
| "mFrameworkClass", Modifier.PRIVATE, Modifier.FINAL); |
| |
| classBuilder.addMethod( |
| MethodSpec.constructorBuilder() |
| .addModifiers(Modifier.PUBLIC) |
| .addParameter(ClassName.get(frameworkClass), "frameworkClass") |
| .addCode("mFrameworkClass = frameworkClass;") |
| .build() |
| ); |
| |
| for (ExecutableElement method : methods) { |
| MethodSpec.Builder methodBuilder = |
| MethodSpec.methodBuilder(method.getSimpleName().toString()) |
| .returns(ClassName.get(method.getReturnType())) |
| .addModifiers(Modifier.PUBLIC) |
| .addAnnotation(Override.class); |
| |
| MethodSignature signature = MethodSignature.forMethod(method, |
| processingEnv.getElementUtils()); |
| |
| for (TypeMirror thrownType : method.getThrownTypes()) { |
| methodBuilder.addException(ClassName.get(thrownType)); |
| } |
| |
| List<String> paramNames = new ArrayList<>(); |
| |
| for (VariableElement param : method.getParameters()) { |
| String paramName = param.getSimpleName().toString(); |
| |
| ParameterSpec parameterSpec = |
| ParameterSpec.builder(ClassName.get(param.asType()), paramName).build(); |
| |
| paramNames.add(paramName); |
| |
| methodBuilder.addParameter(parameterSpec); |
| } |
| |
| String frameworkClassName = "mFrameworkClass"; |
| |
| if (method.getModifiers().contains(Modifier.STATIC)) { |
| frameworkClassName = frameworkClass.getQualifiedName().toString(); |
| } |
| |
| if (signatureReturnOverrides.containsKey(signature)) { |
| methodBuilder.returns(signatureReturnOverrides.get(signature)); |
| methodBuilder.addStatement( |
| "return new $TImpl($L.$L($L))", |
| signatureReturnOverrides.get(signature), frameworkClassName, |
| method.getSimpleName(), String.join(", ", paramNames)); |
| } else if (method.getReturnType().getKind().equals(TypeKind.VOID)) { |
| methodBuilder.addStatement( |
| "$L.$L($L)", |
| frameworkClassName, method.getSimpleName(), String.join(", ", paramNames)); |
| } else { |
| methodBuilder.addStatement( |
| "return $L.$L($L)", |
| frameworkClassName, method.getSimpleName(), String.join(", ", paramNames)); |
| } |
| |
| classBuilder.addMethod(methodBuilder.build()); |
| } |
| |
| writeClassToFile(packageName, classBuilder.build()); |
| } |
| |
| private Set<ExecutableElement> filterMethods( |
| Set<ExecutableElement> allMethods, Apis validApis, Elements elements) { |
| Set<ExecutableElement> filteredMethods = new HashSet<>(); |
| |
| for (ExecutableElement method : allMethods) { |
| MethodSignature methodSignature = MethodSignature.forMethod(method, elements); |
| if (validApis.methods().contains(methodSignature)) { |
| if (method.getModifiers().contains(Modifier.PROTECTED)) { |
| System.out.println(methodSignature + " is protected. Dropping"); |
| } else { |
| filteredMethods.add(method); |
| } |
| } else { |
| System.out.println("No matching public API for " + methodSignature); |
| } |
| } |
| |
| return filteredMethods; |
| } |
| |
| private void writeClassToFile(String packageName, TypeSpec clazz) { |
| String qualifiedClassName = |
| packageName.isEmpty() ? clazz.name : packageName + "." + clazz.name; |
| |
| JavaFile javaFile = JavaFile.builder(packageName, clazz).build(); |
| try { |
| JavaFileObject builderFile = |
| processingEnv.getFiler().createSourceFile(qualifiedClassName); |
| try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { |
| javaFile.writeTo(out); |
| } |
| } catch (IOException e) { |
| throw new IllegalStateException("Error writing " + qualifiedClassName + " to file", e); |
| } |
| } |
| |
| private Set<ExecutableElement> getMethods(TypeElement interfaceClass, Elements elements) { |
| Map<String, ExecutableElement> methods = new HashMap<>(); |
| getMethods(methods, interfaceClass, elements); |
| return new HashSet<>(methods.values()); |
| } |
| |
| private void getMethods(Map<String, ExecutableElement> methods, TypeElement interfaceClass, |
| Elements elements) { |
| interfaceClass.getEnclosedElements().stream() |
| .filter(e -> e instanceof ExecutableElement) |
| .map(e -> (ExecutableElement) e) |
| .filter(e -> !methods.containsKey(e.getSimpleName().toString())) |
| .filter(e -> e.getModifiers().contains(Modifier.PUBLIC)) |
| .forEach(e -> { |
| methods.put(methodHash(e), e); |
| }); |
| |
| interfaceClass.getInterfaces().stream() |
| .map(m -> elements.getTypeElement(m.toString())) |
| .forEach(m -> getMethods(methods, m, elements)); |
| } |
| |
| private String methodHash(ExecutableElement method) { |
| return method.getSimpleName() + "(" + method.getParameters().stream() |
| .map(p -> p.asType().toString()).collect( |
| Collectors.joining(",")) + ")"; |
| } |
| } |