Allow to disable Oreo Autofill service for a package - android

I am developer of a password manager app which provides an Android Autofill service (Android 8+).
Some users requested that it should be possible to disable this service on a per-target-app basis. In the autofill service's onFillRequest I am adding a "dataset" with "Disable autofill for [package name]" like this:
var sender = IntentBuilder.GetDisableIntentSenderForResponse(this, query, isManual, isForDisable);
RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName,
GetString(Resource.String.autofill_disable , new Java.Lang.Object[] { query}), Resource.Drawable.ic_menu_close_grey);
var datasetBuilder = new Dataset.Builder(presentation);
datasetBuilder.SetAuthentication(sender);
foreach (var autofillId in autofillIds)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
}
responseBuilder.AddDataset(datasetBuilder.Build());
When the user clicks the "Disable dataset", an activity is launched which stores the package for which Autofill should be disabled and then immediately finishes itself.
My question: what should I return as a reply from that activity to indicate that Autofill should be invisible from now on?
I am currently doing
bool isManual = Intent.GetBooleanExtra(ChooseForAutofillActivityBase.ExtraIsManualRequest, false);
Intent reply = new Intent();
FillResponse.Builder builder = new FillResponse.Builder();
AssistStructure structure = (AssistStructure)Intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure);
StructureParser parser = new StructureParser(this, structure);
try
{
parser.ParseForFill(isManual);
}
catch (Java.Lang.SecurityException e)
{
Log.Warn(CommonUtil.Tag, "Security exception handling request");
SetResult(Result.Canceled);
return;
}
AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
var autofillIds = autofillFields.GetAutofillIds();
builder.SetIgnoredIds(autofillIds);
Bundle state = new Bundle();
state.PutStringArray("AutoFillDisabledQueries", disabledValues.ToArray());
builder.SetClientState(state);
try
{
var response = builder.Build();
reply.PutExtra(AutofillManager.ExtraAuthenticationResult, response);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
throw;
}
SetResult(Result.Ok, reply);
But
1.) The prompt for autofill does not disappear
2.) if I click disable again, the target app is force-closed (see end of message for details)
so that's obviously not the way to go... Any ideas? Thanks a lot!
Regarding point 2 above, I see the following in logcat:
12-17 09:48:31.865 Google Pixel Error 11711 AndroidRuntime java.lang.RuntimeException: Failure delivering result ResultInfo{who=#android:autoFillAuth:, request=16121857, result=-1, data=Intent { (has extras) }} to activity {com.sonelli.juicessh/com.sonelli.juicessh.activities.ManageConnectionActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:4361)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4403)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1809)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6680)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

setIgnoredIds() should be used for fields that are not autofillable (for example, if the screen had an username, password, and captcha fields, you could use to ignore the latter).
If you want to disable autofill for the activity, you need to return a Fillresponse with just the [disableAutofill()][1] set on it. Notice that disabling it doesn't persist after reboots, so you should keep track of it internally, and call that API if the activity triggers autofill again.

Related

How can I know that my app was opened by Google Assistant, instead of just normally launched

How can I know that my app was opened by Google Assistant, instead of just normally launched.
I don't need App Actions. I just want to know, that yes, my app was opened with "Ok Google -> Open appname" instead of pressing on the icon, or resuming it from recents.
If there an intent/any data in the bundle that I can check for that?
This is my intent when I do "Open appname"
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.xelion.android cmp=com.xelion.android/.activity.InitializationActivity (has extras) }
And it has extras, but don't know what:
Bundle[mParcelledData.dataSize=220]
EDIT:
I found out that this will be the flag for opening with google Assistant:
intent.flags == 0x10000000
But my problem is that this also will run when I build the app from machine or update it, Any idea how to avoid that?
EDIT2:
I have also tried:
private fun getReferrerCompatible(activity: Activity): Uri? {
val intent = activity.intent
val referrerUri: Uri? = intent.getParcelableExtra(Intent.EXTRA_REFERRER)
if (referrerUri != null) {
return referrerUri
}
val referrer = intent.getStringExtra(REFERRER_NAME)
if (referrer != null) {
// Try parsing the referrer URL; if it's invalid, return null
try {
return Uri.parse(referrer)
} catch (e: ParseException) {
return null
}
}
return null
}
But I still get NULL as referrer
I am trying the : intent.extras?.get(KEY_REF_NAME) == REG_G_ASSISTANT or getReferrerCompatible() from the onCreate. Should it be later? like onResume?
When opened through Google Assistant, the android.intent.extra.REFERRER_NAME will be android-app://com.google.android.googlequicksearchbox/https/www.google.com
val KEY_REF_NAME = "android.intent.extra.REFERRER_NAME"
val REG_G_ASSISTANT = "android-app://com.google.android.googlequicksearchbox/https/www.google.com"
if (intent.extras?.get(KEY_REF_NAME) == REG_G_ASSISTANT) {
// APP OPENED THROUGH GOOGLE ASSISTANT
} else {
// APP OPENED THROUGH DEFAULT LAUNCHER
}
Based on the response that theapache64 gave and this link:
https://github.com/allegro/slinger/blob/master/slinger/src/main/java/pl/allegro/android/slinger/ReferrerMangler.java
Because the intent was returning null on Android 10, and due to my min SDK being 23 (I do not need to implement logic for under M), I have done the following code:
val REG_G_ASSISTANT = "com.google.android.googlequicksearchbox"
if (referrer != null && referrer.toString().contains(REG_G_ASSISTANT)) {
//code to do
}
This being Kotlin, and being in an activity. The equivalent of referrer in .java would be:
activity.getReferrer();
In case you run an OS under 23, the referrer can be taken like this:
val KEY_REF_NAME = "android.intent.extra.REFERRER_NAME"
intent.extras?.get(KEY_REF_NAME)
Being that theapache64 tried on a OnePlus6, I assume this should work until API level 28 (Pie) on some devices. But to be sure, I would recommend using the activity.getReferrer()

getContentResolver().delete() throws IllegalArgumentException sporadically

We released a beta of our product, it works for most users, but one user with OnePlus 5T/Android 8.1 reports a sporadic exception:
java.lang.RuntimeException: Unable to resume activity {com.ourproduct/com.ourproduct.MainActivity}: java.lang.IllegalArgumentException: Unknown URL content://com.ourproduct.data/delete
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3726)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3766)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1737)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6753)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:482)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by:java.lang.IllegalArgumentException: Unknown URL content://com.ourproduct.data/delete
at android.content.ContentResolver.delete(ContentResolver.java:1745)
<... here is our code stacktrace ...>
I contacted the user and got some additional info: the issue does not happen every time he uses the operation, just sometimes.
The content provider is defined in the same application.
The code is trivial, just for completeness of the question:
protected void unsetValue(final String group, final String name) {
myContext.getContentResolver().delete(
uri("delete"), null, new String[] { group, name }
);
}
private Uri uri(String location) {
return Uri.parse("content://com.ourproduct.data/" + location);
}
Any ideas? Maybe I can ask the user for some additional info?

Unable to add media item to the queue using Media Controller

I am trying to create a Podcast player. So following the Android Universal Media Player source code. The problem I am facing is, I do not know my playlist ahead of time, and the playlist creation is upon the user interaction. So I need to add/ remove Media Items from my Activity/ Fragment. My code is very alike the UMP sample app, also I have modified the MusicService's mediaSession
from
// Create a new MediaSession.
mediaSession = MediaSessionCompat(this, "MusicService")
.apply {
setSessionActivity(sessionActivityPendingIntent)
isActive = true
}
to
// Create a new MediaSession.
mediaSession = MediaSessionCompat(this, "MusicService")
.apply {
setSessionActivity(sessionActivityPendingIntent)
isActive = true
setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
}
And I am trying to Add an item to the queue like below
fun addRandomMedia() {
if (mediaSessionConnection.isConnected.value == true) {
val mediaDescription = MediaDescriptionCompat.Builder()
.setMediaId("wake_up_01")
.setTitle("Intro - The Way Of Waking Up (feat. Alan Watts)")
.setMediaUri(Uri.parse("https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/01_-_Intro_-_The_Way_Of_Waking_Up_feat_Alan_Watts.mp3"))
.build()
mediaSessionConnection.mediaController.addQueueItem(mediaDescription)
}
}
But I am always getting this below exception
Process: com.example.android.uamp.next, PID: 8446
java.lang.UnsupportedOperationException: This session doesn't support queue management operations
at android.support.v4.media.session.MediaControllerCompat$MediaControllerImplApi21.addQueueItem(MediaControllerCompat.java:1988)
at android.support.v4.media.session.MediaControllerCompat.addQueueItem(MediaControllerCompat.java:316)
at com.example.android.uamp.viewmodels.MainActivityViewModel.addItemToQueue(MainActivityViewModel.kt:118)
at com.example.android.uamp.MainActivity$onCreate$3.onClick(MainActivity.kt:68)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Here is the addQueueItem code form support v4 media package
#Override
public void addQueueItem(MediaDescriptionCompat description) {
long flags = getFlags();
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
throw new UnsupportedOperationException(
"This session doesn't support queue management operations");
}
Bundle params = new Bundle();
params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
sendCommand(COMMAND_ADD_QUEUE_ITEM, params, null);
}
So, I feel must be issues with getFlags method or I am missing something here, as the if check is always true.
To reproduce this issue, I have forked the sample and added the above code in the app. Here is the full code link
Here is the code to reproduce
Are u using MediaSessionConnector?
The problem is the flags are overridden in constructor of MediaSessionConnector.
So u need to call mediaSession.setFlags(flags) after creating an instance of MediaSessionConnector.
mediaSessionConnector = MediaSessionConnector(mediaSession).also {
...
...
it.mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
}

Android Pie (9.0) WebView in multi-process

Starting Android Pie (API 28), Google isn't allowing using a single WebView instance in 2 different processes.
Documentation: https://developer.android.com/reference/android/webkit/WebView.html#setDataDirectorySuffix(java.lang.String)
As required, I called WebView.setDataDirectorySuffix("dir_name_no_separator") but unfortunately, I get an exception.
I tried to call this method inside the 2nd process Service onCreate().
java.lang.RuntimeException: Unable to create service com.myapp.service.MyService: java.lang.IllegalStateException: Can't set data directory suffix: WebView already initialized
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3544)
at android.app.ActivityThread.access$1300(ActivityThread.java:199)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.IllegalStateException: Can't set data directory suffix: WebView already initialized
at android.webkit.WebViewFactory.setDataDirectorySuffix(WebViewFactory.java:136)
at android.webkit.WebView.setDataDirectorySuffix(WebView.java:2165)
at com.myapp.service.MyService.onCreate(MyService.java:134)
I couldn't find any reason for that exception. I didn't call this method twice nor I called it in my main process. Any ideas?
Solved.
My project hosts AdMob ads and I call the MobileAds.initialize() method inside my Application class onCreate(). The ads initializer loads a WebView which is now forbidden to do in a new process before you call the WebView.setDataDirectorySuffix("dir_name_no_separator") method.
When the second process is created, it also goes through the same application create flow, meaning it calls the same onCreate() inside the Application class, which calls the MobileAds.initialize() that tries to create a new WebView instance and by that causes the crash.
IllegalStateException: Can't set data directory suffix: WebView already initialized
How I solved this?
I get the process name using the below method and check if it's my main process - call the MobileAds.initialize() method and if it's my second process, call the
WebView.setDataDirectorySuffix("dir_name_no_separator") method.
Get process name:
public static String getProcessName(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == android.os.Process.myPid()) {
return processInfo.processName;
}
}
return null;
}
Application class onCreate():
if (!Utils.getProcessName(this).equals("YOUR_SECOND_PROCESS_NAME")) {
MobileAds.initialize(this);
} else {
WebView.setDataDirectorySuffix("dir_name_no_separator")
}
To summarize the fix with all the improvements, this is the code in Kotlin:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (packageName != Application.getProcessName()) {
WebView.setDataDirectorySuffix(Application.getProcessName())
}
}
Add it to your Application class to onCreate() method.
Note this is will only fix problem with maximum 2 processes. If your app is using more, you have to provide different WebView suffix for each of them.
when error due to ads, then in application class
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val process = getProcessName()
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
MobileAds.initialize(this)
AudienceNetworkAds.initialize(this)
} catch (e: Error) {
Timber.e(e)
} catch (e: Exception) {
Timber.e(e)
}

React Native Android app crash when receiving push notification on foreground

App is working perfectly fine when receiving push notifications on background, but it crashes when in foregrond. I am using RN 0.49 and react-native-fcm 11.3.1 package.
My code looks as below:
FCM.on(FCMEvent.Notification, async (notif) => {
// there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload
if(notif.local_notification){
// return;
console.log("1111");
this.showLocalNotification(notif);
}
if(notif.opened_from_tray){
//iOS: app is open/resumed because user clicked banner
//Android: app is open/resumed because user clicked banner or tapped app icon
// return;
console.log("2222");
this.showLocalNotification(notif);
}
// await someAsyncCall();
if(Platform.OS ==='ios'){
//optional
//iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application.
//This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override
//notif._notificationType is available for iOS platfrom
switch(notif._notificationType){
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData) //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All) //other types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, (token) => {
console.log("---------Refresh Token-------------")
console.log(token)
// fcm token may not be available on first load, catch it here
});
showLocalNotification(notif) {
FCM.presentLocalNotification({
title: notif.title,
body: notif.body,
priority: "high",
click_action: notif.click_action,
show_in_foreground: true,
local: true
});
}
componentDidMount(){
// iOS: show permission prompt for the first call. later just check permission in user settings
// Android: check permission in user settings
FCM.requestPermissions().then(()=>console.log('granted')).catch(()=>console.log('notification permission rejected'));
FCM.getFCMToken().then(token => {
console.log(token)
// store fcm token in your server
});
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
console.warn("listening.......................");
});
// initial notification contains the notification that launchs the app. If user launchs app by clicking banner, the banner notification info will be here rather than through FCM.on event
// sometimes Android kills activity when app goes to background, and when resume it broadcasts notification before JS is run. You can use FCM.getInitialNotification() to capture those missed events.
// initial notification will be triggered all the time even when open app by icon so send some action identifier when you send notification
FCM.getInitialNotification().then(notif => {
console.log(notif)
});
}
MainApplication.java
public class MainApplication extends MultiDexApplication {
// Needed for `react-native link`
public List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
// Add your own packages here!
// TODO: add cool native modules
// new MainReactPackage(),
new RNBackgroundGeolocation(),
// Needed for `react-native link`
new FIRMessagingPackage(),
//new RNBackgroundGeolocation(),
//new RNFirebasePackage(),
new VectorIconsPackage()
//new RNFirebaseMessagingPackage()
);
}
}
Android Studio gives this error:
java.lang.ClassCastException: x.y.z.MainApplication
cannot be cast to com.facebook.react.ReactApplication
at
com.evollu.react.fcm.MessagingService$1.run(MessagingService.java:41)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6682)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
I don't know what it is causing the app that it is crashing only on foreground state when the app is open!
Any idea on how to solve this problem?

Categories

Resources