Well it appears to be the case for our customers when analyzing logs and symtoms.
As part of the login process to our app the user has a unique registrationId that server side connects the apps device to the user account.
The full details of the login process is irrelevant for the problem, the important thing is that for this to work the user registrationId must be recognized by the server and deemed active.
If the user uninstalls the app and installs it again it will get a new registrationId from the server and server side this mapping is stored in a db table, simplified:
accountId
registrationId
deviceInfo
pushNotificationId
created
deleted
a1
r1
samsung SM-G900F
p1
2020
2021
a1
r2
iPhone 12 pro max
p2
2020
2021
a1
r3
samsung SM-G900F
p3
2021
null
a1
r4
iPhone 12 pro max
p4
2021
null
Again simplified the deleted column is used to determine what devices to send notifications to.
In the app the registrationId is stored in SharedPreferences in MODE_PRIVATE and on an update of the app the registrationId is kept unchanged(at least it used to be).
SharedPreferences.Editor editor = context.getSharedPreferences(FondguidePreferences.class.getSimpleName(), Context.MODE_PRIVATE).edit();
editor.putString("registrationId", "r3");
editor.commit();
So, to the problem at hand. What suddenly happened after the latest release where targetSdkVersion was bumped from 30 to 31 and Java version from VERSION_1_8 to VERSION_11 is that thousands of customers apps suddenly started to call the server using an old registrationId that is marked as deleted server side and had not been used in years and therefore the login process is interrupted and the users cannot access the service.
We can see in our server logs and audit logs that, if we use the db table as example, user with account a1 has initiated login using the registrationId r3 from her samsung(and occasionally r4 from her iPhone app) hundreds of times during the years 2021-2023 so if r3 was not correctly stored in SharedPreferences this would not have been possible.
During that time the app has been updated several times without problem and r3 has still been fetched from SharedPreferences and used for initiating login.
Then all of a sudden after updating to the latest app version last month the samsung reverted back to using r1 to initiate login. The iPhone version of the app still uses the latest though(r4). We do not use flutter or similar so the two code bases are very different but only the android version suffer from this.
So given what we see it seems that after updating, the app suddenly has gotten hold of the oldest version of the SharedPreferences but how is that even possible?
As far as I know there is no versioning of the /data/data/ourapp/shared_prefs/ourapp.xml where old values could be stored?
I have seen for instance this thread SharedPreferences are not being cleared when I uninstall but our customers has just updated their apps and not uninstalled/installed(if they had there would be a new entry in the table with created=2023).
And even if they did why would the SharedPreferences from 2020 be picked up from any backup and not the latest that has been in use for years?
Perhaps something similar to SharedPreferences lost after app update but here the settings are not completely lost which in our case actually would have been a lot better as that would mean a new registrationId would have been created(as it would appear as a new install to the app).
As we have no idea what is wrong, so far we have simply made the old registrationIds valid to initiate login with as well as the new ones. So most users can now login although there are cases where old devices, and thereby registrationIds, have been deleted from the database(for instance for GDPR reasons) so in those cases the user is not recognized by the server at all and can not login as the device cannot be mapped to an account to fetch further login information from.
Any ideas of what may have happened are greatly appreciated!
Related
My question is, I have to update an app that's on the appstore when they download the update will all the data also be downloaded automatically or not, my app is a calendar with events and a recyclerView with there events and the update is to update the calendar range from last year to this year (2019/01/01 - 2020/01/30) => (2020/01/01 - 2021/01/30) but doing this also means I need to download the calendar events again.
My download function is in the MainActivity.java and it downloads a json from online.
Is there a way to force this download when the person updates the app?
EDIT: This is the section of code that changes in this update, the widget in this case is this dependency
widget.state().edit()
.setMinimumDate(CalendarDay.from(2020, 1, 1))
.setMaximumDate(CalendarDay.from(2021, 1, 31))
.commit();
This also has sdk versions dependencies as per https://developer.android.com/guide/topics/data/autobackup, but generally speaking two possibilities exist:
Your previous app is already installed and used on the device
This is clean install on new device of user who was using your app on other device
All the cached data for p1 will be there for your scenario.
P2 depends on your manifest configuration and if device does backups: https://developer.android.com/guide/topics/manifest/application-element#allowbackup
If you application manifest says allowBackups true and device was configured/able to backup previous app version, clean install for user who was using your app on device A will get shared prefs, local storage etc (https://developer.android.com/guide/topics/data/autobackup) to device B upon install of the app. Summing up, if you are using any of the storages mentioned in autobackup url, they will be restored and ready for use.
Environment:
Ejabberd Version : 16.04
Smack-android-4.1.0
I'm working on an Android chat application. Currently, same user credentials can be used login from multiple devices.
The current scenario is as follows:
1. User logs in into the app in device A
2. Using the same username and password, the user logs successfully into the app in device B
3. Now device A says, it is disconnected, but continue the chat in device B
However, according to the given requirement, it should behave like this:
1. User logs in into the app in device A
2. Using the same username and password, when the user tries to log in from device B, it should not allow it.
(Since he is already logged in from device A)
Would be glad to hear your solutions/ideas on this. Thanks in advance.
So I managed to resolve the problem using the option resource_conflict
According to Ejabberd Configuring Docs
The option resource_conflict defines the action when a client attempts
to login to an account with a resource that is already connected. The
option syntax is:
resource_conflict: setresource|closenew|closeold: The possible values
match exactly the three possibilities described in XMPP Core: section
7.7.2.2. The default value is closeold. If the client uses old Jabber Non-SASL authentication (XEP-0078), then this option is not respected,
and the action performed is closeold.
So open ejabberd.yml and add the following line to that file.
resource_conflict: closenew
Then restart the ejabberd server.
Now it will disallow the resource binding attempt of the newly connecting client and maintain the session of the currently connected client.
References:
https://www.rfc-editor.org/rfc/rfc6120#section-7.7.2.2
Read #rubycon's answer on this- https://stackoverflow.com/a/51860779/5361779
From XMPP spec:
"If there is already an active resource of the same name, the server MUST either (1) terminate the active resource and allow the newly-requested session, or (2) disallow the newly-requested session and maintain the active resource. Which of these the server does is up to the implementation, although it is RECOMMENDED to implement case #1."
More info here https://xmpp.org/rfcs/rfc3921.html#session
So your current scenario is a recommended one.
However, I have quickly checked for ejabberd src code and found it can be configured somehow (closeold -> closenew)
https://github.com/processone/ejabberd/blob/master/src/ejabberd_c2s.erl#L964
https://github.com/processone/ejabberd/blob/master/src/ejabberd_c2s.erl#L873
I'm not an Erlang specialist, but looks like it can be achieved by modifying the source code
If device B sets as resource one different than device A, both can be connected to the same account correctly. In your tests, device B sets the exact same resource than device A, and then ejabberd kicks the older session.
I see there's an option to limit the number of sessions an account can have active in the server. The problem is that it kicks the older session, but you would like to disallow the new login. See
https://docs.ejabberd.im/admin/configuration/#limiting-opened-sessions-with-acl
I am working on the debug version of an android app. I was able to send both SMS and email invites until I recently installed a different OS on my machine (both Linux). As a result, I also updated Android Studio to the latest version, and imported my old project settings. The rest of the application works fine, I can also start the activity for selecting invites, it displays the message that Invitation was sent, but AppInviteInvitation.getInvitationIds returns 0 length array in onActivityResult.
I tried to add Sha1 and Sha256 to the app in the Firebase console, removed Firebase completely and then added it back to the android project in Android Studio (also removed and added again application and project in the firebase console). So checked and tried most of the solutions on SO, but none seems to work. I am probably omitting something. I am using two google accounts, one for firebase, one for email sending from device.
What else could I verify? Is there any way to dig deeper in Firebase invites in order to find the issue?
The intent:
private void onInviteClicked() {
Intent intent = new AppInviteInvitation.IntentBuilder("MyApp")
.setMessage("Some message of 90 nospecial chars")
// .setDeepLink(createDynamicLink(2))
.setCustomImage(Uri.parse("http://correct url"))
.setCallToActionText("Call to action")
.build();
startActivityForResult(intent, REQUEST_INVITE);
}
Short: keep message short (tested with < 40 chars)
Long: My intent actually contained a 90 character message (no special chars), which was sent just fine before the update. After the update, I need to reduce the length of the message (tested with ~40 characters, none is special), in order for it to be sent.
I wonder what determined this behavior. Things that were changed: java (previously was oracle jdk, now it is the java that comes by default with Android Studio); Maybe also java version. Build tools version 25.0.2 -> 26.0.0.
My login system uses SharedPreferences to store information about the user. The main two variables for the login system are:
loggedin - boolean
userID - int of the userID that's logged in (primary key on the DB)
When the user logs in, loggedin is set to 1 and the userID is set to the user ID fetched from the DB. When the logout button is pressed loggedin is set to 0 and userID set to null.
Situations:
I log in, close the app and repoen it = fine, still logged in to the correct account
I log in, then logout, close & reopen the app = fine, comes up with login screen
I carry out invalidate caches and restart in android studio = fine, stays logged in on the correct account.
Now here's the one that's going wrong: If I login, then uninstall the app off my phone and press run on android studio the app launches and logs in to a really old account that no longer exists on the DB; and I can't work out why this is happening.
My only thought is there is a userID stored on the device that isn't being removed, but that could be completely wrong. Any ideas?
I've added some log tags throughout the code and before the app is uninstalled the userID is correct and when it's been reinstalled it's the old one.
Shared Preferences are always cleared along with uninstalling app.
But since android-21 Backup task stores preferences by default to cloud. Later when you uninstall then install newer version .You are probably going to use restored preferences. To avoid that, just add this to your manifest ( or at least manifest for debug). -
<application ...
android:allowBackup="false">
...
</application>
Read this:http://developer.android.com/guide/topics/data/backup.html
It's Important to mention here that process of backup is blackbox .. you don't know when it starts, and period between checks ... so better for developing to disable it.
I've experienced a strange thing in my Parse app: a new Installation record has been added, from an Android device, but its deviceToken field is blank. Probably for this, the new user doesn't receive any push notification (subscribers targeting his device are always zero).
Why this situation?
You may also use installation table just to track installation of app across user devices, for example to handle loading data from other device ...