My app that uses Google Maps SDK (v2) just started crashing with this exception:
Process: com.currentlocation.android, PID: 7328
java.lang.ArrayIndexOutOfBoundsException: length=1; index=12
at com.google.maps.api.android.lib6.gmm6.vector.ct.<init>(:com.google.android.gms.dynamite_mapsdynamite#201216081#20.12.16 (120400-0):9)
at com.google.maps.api.android.lib6.gmm6.vector.cv.a(:com.google.android.gms.dynamite_mapsdynamite#201216081#20.12.16 (120400-0):23)
at com.google.maps.api.android.lib6.gmm6.util.m.run(:com.google.android.gms.dynamite_mapsdynamite#201216081#20.12.16 (120400-0):14)
at java.lang.Thread.run(Thread.java:919)
In the v3 beta SDK, the stack trace is:
2020-04-23 15:59:06.064 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example, PID: 22717
java.lang.ArrayIndexOutOfBoundsException: length=1; index=12
at com.google.android.libraries.maps.bv.zzbs.<init>(ZoomTable.java:24)
at com.google.android.libraries.maps.bv.zzbv.zza(ZoomTableQuadTree.java:57)
at com.google.android.libraries.maps.br.zzd.zza(Unknown Source:4)
at com.google.android.libraries.maps.hi.zzas.zza(Suppliers.java:7)
at com.google.android.libraries.maps.br.zza.zzh(SharedMapComponentImpl.java:58)
at com.google.android.libraries.maps.gu.zzat.zza(RendererFactoryImpl.java:88)
at com.google.android.libraries.maps.it.zzav.zza(GoogleMapImpl.java:59)
at com.google.android.libraries.maps.it.zzci.zza(MapFragmentDelegateImpl.java:3)
at com.google.android.libraries.maps.it.zzcg.zza(MapFragmentDelegateImpl.java:15)
at com.google.android.libraries.maps.SupportMapFragment$zza.onCreateView(SupportMapFragment.java:15)
at com.google.android.gms.dynamic.zae.zaa(com.google.android.gms:play-services-base##17.1.0:4)
at com.google.android.gms.dynamic.DeferredLifecycleHelper.zaa(com.google.android.gms:play-services-base##17.1.0:9)
at com.google.android.gms.dynamic.DeferredLifecycleHelper.onCreateView(com.google.android.gms:play-services-base##17.1.0:25)
at com.google.android.libraries.maps.SupportMapFragment.onCreateView(SupportMapFragment.java:34)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:310)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1185)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2617)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2569)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2722)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:336)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1186)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2222)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1995)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1951)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2621)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2569)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2722)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:336)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1186)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2617)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2569)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1432)
at android.app.Activity.performStart(Activity.java:7848)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3294)
2020-04-23 15:59:06.064 E/AndroidRuntime: at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
What is the cause?
Edit: following is the official solution from Google (link)
Summary
Google Maps SDK thread crashes App (ArrayIndexOutOfBoundsException) - Solution Offered
Description
On April 23 2020 starting at 11:30 PDT, Google served for 4 hours an update to the configuration of a Maps mobile component, triggering crashes in Maps SDKs for Android and iOS. Applications on devices that downloaded this version of the configuration (during the outage period) were vulnerable to the crash. Workaround solutions are offered for Maps SDKs for Android and iOS.
Maps SDK for Android
Maps SDK for Android v2 (included in Google Play Services)
The updates to Google Play Services to fix the crash has been published to all devices with Google Play Services version 17.4.55 and newer. There is no change to the version number of Google Play Services on the device after the update is installed. No action is required from developers or end users to receive the updated Maps module; however, developers can verify that the module is present on a given device with the following adb command:
adb shell dumpsys activity provider com.google.android.gms.chimera.container.GmsModuleProvider
You should see the line Module Set ID: maps listed in the Module Sets section.
Module Set ID: maps, Module Set Version: 2015120015120000
The crash rates of Maps SDK for Android v2 are back to normal.
As of now, if you have not updated your app with the client-side code workarounds mentioned below, you do not need to take further action.
If you have already updated your app with the workarounds, you can remove the workaround in a subsequent update of your app (but keeping the workaround is safe).
Premium Plan Maps SDK for Android v2 or Maps SDK for Android v3 beta (static libraries)
If your app uses the Premium Plan Maps SDK for Android v2 or Maps SDK for Android v3 beta (static libraries), and is still experiencing crashes, we still highly recommend you to roll out the workarounds below via an update to your app. As your application is loading a static version of the SDK which is vulnerable to the bad data being stored on some devices, only an update to your application can solve the problem.
Play Store review approvals
If you update your app but experience Play Store review approvals delays, please file a support case with your app’s Package ID: Contact the support team. Our Support Team will internally escalate your request and expedite the approval.
Negative reviews in the Google Play Store
Some application developers inquired about 1-star reviews in the Google Play Store left by end-users due to crashes. Only comments that violate Google Play's policy [ 1 ] can be removed. You can also flag abusive reviews in the Play Console [ 2 ]. Applications will not be automatically removed from the Google Play store due to negative reviews. It's also worth noting that the calculation of your overall app review rating favors recent reviews, which means that your rating will recover to pre-incident levels over time.
[ 1 ] Ratings & Review on the Play Store
[ 2 ] Report inappropriate reviews
Maps SDK for iOS
Crash rates on iOS are back to normal. If your application is still experiencing crashes, you need to update and publish your app with the code workarounds communicated here.
For questions about deploying or expediting your application in the Apple App Store, please contact Apple directly.
With this update, we are closing this issue. Thank you to everyone for your patience. Our team is performing an in-depth internal investigation of this incident; as soon as possible, we will publish our analysis (in approximately a week). In the meantime, if you have any questions, or are still experiencing problems, please file a support case.
Workarounds:
End users on Android can clear the affected app's data (not just the
cache).
End users on iOS can uninstall then reinstall the affected app(s).
App Developers can apply the code workarounds below in order to solve
the issue for all their end users.
Code workaround for iOS:
Recommended placement for the code is before GMSServices initialization in the application(_:didFinishLaunchingWithOptions:) (Swift) or application:didFinishLaunchingWithOptions: (Objective-C) method. Specifically:
Swift:
let key = "GoogleMapsServerControlledParamsKey_bug_154855417"
if !UserDefaults.standard.bool(forKey: key) {
let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
if urls.count > 0 {
let paramUrl = urls[0].appendingPathComponent("com.google.GoogleMaps/ServerControlledParams", isDirectory: false)
try? FileManager.default.removeItem(at: paramUrl)
}
UserDefaults.standard.set(true, forKey: key)
}
Objective-C:
NSString *key = #"GoogleMapsServerControlledParamsKey_bug_154855417";
BOOL keyExists = [[NSUserDefaults standardUserDefaults] boolForKey:key];
if (!keyExists) {
NSArray<NSURL *> *array =
[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if (array.count > 0) {
NSURL *url =
[array[0] URLByAppendingPathComponent:#"com.google.GoogleMaps/ServerControlledParams"
isDirectory:NO];
if (url) {
[[NSFileManager defaultManager] removeItemAtURL:url error:NULL];
}
}
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
}
Code workaround for Android:
The recommended placement for the code is in Application.onCreate():
Java
try {
SharedPreferences hasFixedGoogleBug154855417 = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE);
if (!hasFixedGoogleBug154855417.contains("fixed")) {
File corruptedZoomTables = new File(getFilesDir(), "ZoomTables.data");
File corruptedSavedClientParameters = new File(getFilesDir(), "SavedClientParameters.data.cs");
File corruptedClientParametersData =
new File(
getFilesDir(),
"DATA_ServerControlledParametersManager.data."
+ getBaseContext().getPackageName());
File corruptedClientParametersDataV1 =
new File(
getFilesDir(),
"DATA_ServerControlledParametersManager.data.v1."
+ getBaseContext().getPackageName());
corruptedZoomTables.delete();
corruptedSavedClientParameters.delete();
corruptedClientParametersData.delete();
corruptedClientParametersDataV1.delete();
hasFixedGoogleBug154855417.edit().putBoolean("fixed", true).apply();
}
} catch (Exception e) {
}
Kotlin
try {
val sharedPreferences = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE)
if (!sharedPreferences.contains("fixed")) {
val corruptedZoomTables = File(filesDir, "ZoomTables.data")
val corruptedSavedClientParameters = File(filesDir, "SavedClientParameters.data.cs")
val corruptedClientParametersData = File(filesDir, "DATA_ServerControlledParametersManager.data.${packageName}")
val corruptedClientParametersDataV1 = File(filesDir, "DATA_ServerControlledParametersManager.data.v1.${packageName}")
corruptedZoomTables.delete()
corruptedSavedClientParameters.delete()
corruptedClientParametersData.delete()
corruptedClientParametersDataV1.delete()
sharedPreferences.edit().putBoolean("fixed", true).apply()
}
} catch (exception: Exception) {
}
The workarounds provided here covers all available flavors and versions of our SDKs for Android. To clarify further (in case you released an earlier version of the workaround that did not delete as many files):
Applications that use Maps Android SDK v2 should only need to delete
one file: ZoomTables.data.
Applications that use Maps Android SDK v3 beta should only need to
delete one file, either
DATA_ServerControlledParametersManager.data.v1. +
getBaseContext().getPackageName())
or
DATA_ServerControlledParametersManager.data. +
getBaseContext().getPackageName())
Seems in every app Google Map creates ZoomTables.data file.
This file was malformed, malformed version of it that could be downloaded from comment.
To reproduce the issue remove ZoomTables.data from the app packages on the device and insert malformed one. The app should crash.
Currently, the issue was solved on google's side, but the apps still contain a cached version of that data file.
To fix the issue we should remove that file right on the app start in Application onCreate Method.
private void fixGoogleMapBug() {
try {
SharedPreferences hasFixedGoogleBug154855417 = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE);
if (!hasFixedGoogleBug154855417.contains("fixed")) {
File corruptedZoomTables = new File(getFilesDir(), "ZoomTables.data");
File corruptedSavedClientParameters = new File(getFilesDir(), "SavedClientParameters.data.cs");
File corruptedClientParametersData =
new File(
getFilesDir(),
"DATA_ServerControlledParametersManager.data."
+ getBaseContext().getPackageName());
File corruptedClientParametersDataV1 =
new File(
getFilesDir(),
"DATA_ServerControlledParametersManager.data.v1."
+ getBaseContext().getPackageName());
corruptedZoomTables.delete();
corruptedSavedClientParameters.delete();
corruptedClientParametersData.delete();
corruptedClientParametersDataV1.delete();
hasFixedGoogleBug154855417.edit().putBoolean("fixed", true).apply();
}
} catch (Exception e) {
}
}
Update 1
I've updated workaround, base on the latest google developers comment:
The workarounds provided here covers all available flavors and versions of our SDKs for Android. To clarify further (in case you released an earlier version of the workaround that did not delete as many files):
Applications that use Maps Android SDK v2 should only need to delete one file: ZoomTables.data.
Applications that use Maps Android SDK v3 beta should only need to delete one file, either DATA_ServerControlledParametersManager.data.v1. + getBaseContext().getPackageName()) or DATA_ServerControlledParametersManager.data. + getBaseContext().getPackageName())
This solution worked for me
First open "App Info"
Choose "Storage"
Click "Clear data"
Open app again and check if the issue fixed.
Below are suggestions just to help your customers.
Send an email to your all users for the inconvenience and describe them the issue they faced and give them above mentioned steps to resolve their issue.
You can also send Push Notification to all your users with Firebase Push Notifications, if your app have the push notifications service.
Screenshots demonstration:
Drawbacks of solutions above:
ZoomTables.data is deleted on every device, regardless of whether the device is affected by the crash or not
Workaround only works once, is it sure that this problem never happens again?
Drawbacks of my solution:
on first execution of maps activity on affected device map is empty. After rotation of device or second execution map is shown up
My solution catches the Exception thrown by the Maps SDK, call this in onCreate of the Application class:
public static void catchGoogleMapsException(final Context context)
{
final Thread.UncaughtExceptionHandler defaultHandler =
Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(
(#NonNull final Thread thread, #NonNull final Throwable ex) ->
{
if (thread.getName().contains("ZoomTableManager"))
{
new File(context.getFilesDir(), "ZoomTables.data").delete();
Log.w("Maps Bug 154855417", "Caught exception and deleted ZoomTables.data");
}
else
if (defaultHandler!=null)
defaultHandler.uncaughtException(thread, ex);
else
throw new RuntimeException(
"No default uncaught exception handler.", ex);
});
}
Solution Offered for iOS & Android:
https://issuetracker.google.com/issues/154855417#comment509
Maps SDK for Android v2 (included in Google Play Services)
The updates to Google Play Services to fix the crash has been published to all devices with Google Play Services version 17.4.55 and newer. There is no change to the version number of Google Play Services on the device after the update is installed. No action is required from developers or end users to receive the updated Maps module; however, developers can verify that the module is present on a given device with the following adb command:
adb shell dumpsys activity provider com.google.android.gms.chimera.container.GmsModuleProvider
You should see the line Module Set ID: maps listed in the Module Sets section.
Module Set ID: maps, Module Set Version: 2015120015120000
The crash rates of Maps SDK for Android v2 are back to normal.
As of now, if you have not updated your app with the client-side code
workarounds mentioned below, you do not need to take further action.
If you have already updated your app with the workarounds, you can
remove the workaround in a subsequent update of your app (but keeping
the workaround is safe).
Code workaround for Android:
The productive version was fixed (on google's side) but if you still have problems with your emulator you have to run the following code only once.
The recommended placement for the code is in Application.onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fixGoogleMapBugTemp() //TODO: Then clean this line
// ...
}
/**
* Observation: Invoke this method only once
*/
private fun fixGoogleMapBugTemp() {
val googleBug = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE)
if (!googleBug.contains("fixed")) {
val corruptedZoomTables = File(filesDir, "ZoomTables.data");
corruptedZoomTables.delete();
googleBug.edit().putBoolean("fixed", true).apply();
}
}
Workaround: Clear application data (not just the cache).
Note: A copy of the problematic file from the app packages on the device if anyone needs it for repro.
Source
GL
For Android, multiple developers mentioned a workaround consisting in deleting the ZoomTable.data file directly from their application. After review, this fix seems safe, and you could try it in your application.
Please refer to
https://issuetracker.google.com/154855417#comment179
If you want your users to continue using your app without re-installing,
The sample code is copy-pasted here for your convenience. In Application.onCreate():
SharedPreferences googleBug = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE);
if (!googleBug.contains("fixed")) {
File corruptedZoomTables = new File(getFilesDir(), "ZoomTables.data");
corruptedZoomTables.delete();
googleBug.edit().putBoolean("fixed", true).apply();
}
reference: Google Maps SDK is crashing -- partially resolved
Complete & official answer for all :
Diagnosis: Crash of the Google Maps Platform mobile SDKs (iOS & Android) at load.
Workaround: * Clear the affected app's data (not just the cache), or uninstall then reinstall the affected app(s).
Code workaround for iOS:
Recommended placement for the code is before GMSServices initialization in the application(_:didFinishLaunchingWithOptions:) (Swift) or application:didFinishLaunchingWithOptions: (Objective-C) method. Specifically:
Swift:
let key = "GoogleMapsServerControlledParamsKey_bug_154855417"
if !UserDefaults.standard.bool(forKey: key) {
let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
if urls.count > 0 {
let paramUrl = urls[0].appendingPathComponent("com.google.GoogleMaps/ServerControlledParams", isDirectory: false)
try? FileManager.default.removeItem(at: paramUrl)
}
UserDefaults.standard.set(true, forKey: key)
}
Objective-C:
NSString *key = #"GoogleMapsServerControlledParamsKey_bug_154855417";
BOOL keyExists = [[NSUserDefaults standardUserDefaults] boolForKey:key];
if (!keyExists) {
NSArray<NSURL *> *array =
[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
if (array.count > 0) {
NSURL *url =
[array[0] URLByAppendingPathComponent:#"com.google.GoogleMaps/ServerControlledParams"
isDirectory:NO];
if (url) {
[[NSFileManager defaultManager] removeItemAtURL:url error:NULL]);
}
}
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
}
Once you have deployed it in your app, you can file a Maps Support case if you would like us to help expedite its approval. Please make sure to include your application's ID, Bundle ID, and the version you want reviewed in your case.
Code workaround for Android:
The recommended placement for the code is in Application.onCreate():
Java
try {
SharedPreferences hasFixedGoogleBug154855417 = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE);
if (!hasFixedGoogleBug154855417.contains("fixed")) {
File corruptedZoomTables = new File(getFilesDir(), "ZoomTables.data");
File corruptedSavedClientParameters = new File(getFilesDir(), "SavedClientParameters.data.cs");
File corruptedClientParametersData =
new File(
getFilesDir(),
"DATA_ServerControlledParametersManager.data.v1."
+ getBaseContext().getPackageName());
corruptedZoomTables.delete();
corruptedSavedClientParameters.delete();
corruptedClientParametersData.delete();
hasFixedGoogleBug154855417.edit().putBoolean("fixed", true).apply();
}
} catch (Exception e) {
}
Kotlin
try {
val sharedPreferences = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE)
if (!sharedPreferences.contains("fixed")) {
val corruptedZoomTables = File(filesDir, "ZoomTables.data")
val corruptedSavedClientParameters = File(filesDir, "SavedClientParameters.data.cs")
val corruptedClientParametersData = File(filesDir, "DATA_ServerControlledParametersManager.data.v1.${packageName}")
corruptedZoomTables.delete()
corruptedSavedClientParameters.delete()
corruptedClientParametersData.delete()
sharedPreferences.edit().putBoolean("fixed", true).apply()
}
} catch (exception: Exception) {
}
Source : https://issuetracker.google.com/issues/1548554
You should call the follow method on the onCreate of your Android app to avoid the crash.
private fun clearCorruptedGMapsPreference() {
try {
val sharedPreferences = getSharedPreferences("google_bug_154855417", Context.MODE_PRIVATE)
if (!sharedPreferences.contains("fixed")) {
val corruptedZoomTables = File(filesDir, "ZoomTables.data")
val corruptedSavedClientParameters = File(filesDir, "SavedClientParameters.data.cs")
val corruptedClientParametersData = File(filesDir, "DATA_ServerControlledParametersManager.data.${packageName}")
val corruptedClientParametersDataV1 = File(filesDir, "DATA_ServerControlledParametersManager.data.v1.${packageName}")
corruptedZoomTables.delete()
corruptedSavedClientParameters.delete()
corruptedClientParametersData.delete()
corruptedClientParametersDataV1.delete()
sharedPreferences.edit().putBoolean("fixed", true).apply()
}
} catch (exception: Exception) {
LogUtil.e(this#CourierApplication::class.java.name, "An error has happened. Error: ".plus(exception.cause?.message
?: ""))
}
}
This is working code for Xamarin. You need to install Xamarin.Essentials nuget package.
if (!Preferences.Get("google_bug_fixed", false))
{
var corruptedZoomTables = new File(FileSystem.AppDataDirectory, "ZoomTables.data");
corruptedZoomTables.Delete();
Preferences.Set("google_bug_fixed", true);
}
Workaround suggested will only work one time:
I suggest to send a silent push notification to your app about Google Map Zoom Data Corrupted or make an API call to check the status of the Google Map Zoom Data. If you think this as un-necessary server call every time, you can either use push notification to trigger this logic or integrate with one of your existing calls where you check status of your servers. Leaving this to your app specific logics.Doing so, if Google Maps again throws this error you can re-trigger your work-around.
If server call, this will reset ZoomData for all your users.
If push-notifications, you can send notifications to some users.
fun receivedPushNotificationZoomDataCorrupted() {
try {
val corruptedZoomTables = File(filesDir, "ZoomTables.data")
val corruptedSavedClientParameters = File(filesDir, "SavedClientParameters.data.cs")
val corruptedClientParametersData = File(filesDir, "DATA_ServerControlledParametersManager.data.v1.${packageName}")
corruptedZoomTables.delete()
corruptedSavedClientParameters.delete()
corruptedClientParametersData.delete()
}
} catch (exception: Exception) {
}
}
Related
Making app in Unity.
App works perfectly in Unity editor:
can access firebase for user login - this isnt the issue
can read and write from/to google sheet located in google drive using service account
When I build to android (apk file), and install on physical andoid device (Samsung S20 phone):
can access firebase for user login - so it is not an network/internet problem
CANT read/write from google sheet.
A portion of my code is here:
Authorisation - i am not sure if this bit works on the phone, or how to check.
public void Auth() // authorise access to the googlesheet online
{
//jsonKey = File.ReadAllText("Assets/Resources/Creds/beerhats-db-3***.json"); // location of key - read it - save to string
TextAsset txtAsset = (TextAsset)Resources.Load("beerhats-db-3***", typeof(TextAsset)); //i changed the file name here on stackoverflow just in case its a security thing
string jsonKey = txtAsset.text;
ServiceAccountCredential.Initializer initializer = new ServiceAccountCredential.Initializer(Secrets.serviceAccountID);
var jsonData = (JObject)JsonConvert.DeserializeObject(jsonKey);
if (jsonData != null) // the json file isnt moving or changing - so probably dont need an if statemet - remove later
{
string privateKey = jsonData["private_key"].Value<string>(); // specifically get the private key from all the data in the json file
ServiceAccountCredential credential = new ServiceAccountCredential(initializer.FromPrivateKey(privateKey));
service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
});
}
}
Part of my ReadFile code:
public async Task<string> ReadFile(string sheetName, string cellLocation) // for reading data only
{
// creates a string of location to write to
string whereToRead = sheetName + "!" + cellLocation;
SpreadsheetsResource.ValuesResource.GetRequest request = service.Spreadsheets.Values.Get(Secrets.spreadsheetID, whereToRead);
StringBuilder sb = new StringBuilder();
ValueRange response = await request.ExecuteAsync(); // run this on a separate thread as it takes long?
IList<IList<object>> values = response.Values;
On the phone, it executes the stringbuilder line, but not the executeasync() line.
Tried:
upgrade Unity version
change the min and target API levels
introduced keystore and project keys
introduced async incase it was a loading issue
change custom gradle template checkboxes
change read access on android sdk
update nuget installs of google apis
asking chatgpt for help
looking at various forums for similar issues
I am not running on an emulator, so no help with errors during run time. No errors in editor when built.
Expecting:
when installed on android device, the app to read/write from google sheet located in google drive
Requesting:
ideas to try to get this working! Thankyou :)
I have the current workflow for my authentication
User signs in via google OAuth2
User is then given a server_auth_code which they send to my backend authentication
The auth code is validated on the back end and users is sent a JWT
I had this all working in raw Java with the Android SDK, but Flutter seemed a lot nicer. But now when using the google_sign_in plugin on android, I am unable to retrieve the serverAuthCore anymore, this plugin just wants to return null the entire time.
I Am using the client ID that's specified for Android, however, I tested the WebApplication that's auto-generated by google too but that's the same issue (Null serverAutHCode)
This is the code that I am currently using:
/// Provides the `GoogleSignIn` class
import 'package:google_sign_in/google_sign_in.dart';
class GoogleLoginPage extends StatefulWidget {
final String name = "Logging in with google.";
late GoogleSignIn _googleSignIn;
GoogleLoginPage() : super() {
_googleSignIn = GoogleSignIn(
scopes: ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'],
serverClientId: "XX-XXX.apps.googleusercontent.com"
);
}
Future<void> fetchAuthToken() async {
try {
var response = await _googleSignIn.signIn();
log(response?.serverAuthCode ?? "No server auth code");
_googleSignIn.signOut();
} catch (error) {
print("ERR");
print(error);
}
}
#override
State<GoogleLoginPage> createState() => GoogleLoginState();
}
The output of this code is: [log] No server auth code
The question:
Am I doing something wrong? As mentioned this works 100% on my java project using the google play services SDK so I know it's nothing to do with my google console configurations.
Okay so I figured out the issues:
It appears that by default the google login plugin for flutter comes on an older version (If I remember correctly it was 20.0.5)
I Simply changed the version to the latest version:
'com.google.android.gms:play-services-auth:20.2.0'
You can do this by editing the project's build.gradle (In IntelliJ you open build.gradle and click "Open for editing in the android studio" in the top right, from there you need to find the gradle file for google_sign_in, and change the import there, remember to click sync in the top right of android studio before you close out of it)
And I began to receive my serverAuthCode as normal, cheers!
My current workflow is I publish my apks (4 splits by abi) to alpha in draft. This works fine because I am using Gradle Play Publisher.
The next step is I upload the expansion file, this is a custom gradle task.
I start an edit with an authenticated AndroidPublisher
val editId=publisher.edits().insert(packageName, null).execute().id
For my first apkVariant I upload the expansion file
publisher.edits().expansionfiles()
.upload(
packageName,
editId,
apkVariantOutput.versionCodeOverride,
"main",
FileContent("application/octet-stream",myFile)
).execute()
and for all subsequent variants I reference the new expansion file
publisher.edits().expansionfiles().update(
packageName,
editId,
apkVariantOutput.versionCodeOverride,
"main",
ExpansionFile().apply {
referencesVersion = apkVariants[0].versionCodeOverride
}
).execute()
and finally commit this edit
publisher.edits().commit(packageName, editId).execute()
example output from some prints:
{
"expiryTimeSeconds" : "1573666146",
"id" : "04893130212727174754"
}
setting expansion for 6193171
{
"fileSize" : "590199"
}
{
"referencesVersion" : 6193171
}
{
"referencesVersion" : 6193171
}
{
"referencesVersion" : 6193171
}
{
"expiryTimeSeconds" : "1573666146",
"id" : "04893130212727174754"
}
But the expansion files never show up on the Play console even though the task completes without error.
I hope I am fundamentally misunderstanding something about this API because I can't make sense of this.
I rewrote my entire app upload process with Play Developer API.
You can't upload expansion files to draft, this question is still relevant. I can't find the google issue it refers to but a year later it is evidently still the case.
An application is published on Play Store and it is using 'application data folder' for the backup-restore purpose using Drive API. Everything works fine. However, this API is about to be turned down on 6th December, 2019 according to Google's announcement. Therefore, in order to support existing users, I have been migrating to latest API according to migration guidlines and an official sample app.
I can successfully authenticate using the code (from the official link) below.
GoogleSignInOptions signInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestScopes(new Scope(DriveScopes.DRIVE_APPDATA))
.build();
GoogleSignInClient client = GoogleSignIn.getClient(this, signInOptions);
// The result of the sign-in Intent is handled in onActivityResult.
startActivityForResult(client.getSignInIntent(), REQUEST_CODE_SIGN_IN);
I am also using correct scope - DriveScopes.DRIVE_APPDATA as mentioned in the official documentation.
I am also seeing correct values of 'email' and 'granted scopes' inside onActivityResult()
if (requestCode == REQUEST_CODE_SIGN_IN && resultCode == RESULT_OK) {
GoogleSignIn.getSignedInAccountFromIntent(data).addOnSuccessListener(new OnSuccessListener<GoogleSignInAccount>() {
#Override
public void onSuccess(GoogleSignInAccount googleSignInAccount) {
Log.e("TAG", "Email - " + googleSignInAccount.getEmail()); // prints correct value
Log.e("TAG", "Granted scopes - " + googleSignInAccount.getGrantedScopes()); // prints correct value
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getActivity(), Collections.singleton(DriveScopes.DRIVE_APPDATA));
credential.setSelectedAccount(googleSignInAccount.getAccount());
Drive googleDriveService = new Drive.Builder(
AndroidHttp.newCompatibleTransport(),
new GsonFactory(),
credential)
.setApplicationName("App Name") // Changed it for now
.build();
mDriveServiceHelper = new DriveServiceHelper(googleDriveService);
queryFiles();
}
});
}
However, whenever I try to access a backup file in queryFiles() using the code (from the official link) below,
FileList files = driveService.files().list()
.setSpaces("appDataFolder")
.setFields("nextPageToken, files(id, name)")
.setPageSize(10)
.execute();
for (File file : files.getFiles()) {
System.out.printf("Found file: %s (%s)\n",
file.getName(), file.getId());
}
It throws the following error
{
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
Kindly help me fix the error. I believe that as everything is working fine with the published version, everything should be correct in terms of configuring on Google API console.
I probably can't find ALL threads with this problem but I can try to help in a few or so.
PRIMARY ANSWER
IF you're using ProGuard same as I am. ProGuard can cause this error to happen during query. I fixed it using the following.
# Fix OAuth Drive API failure for release builds
-keep class * extends com.google.api.client.json.GenericJson { *; }
-keep class com.google.api.services.drive.** { *; }
-keepclassmembers class * { #com.google.api.client.util.Key <fields>; }
SECONDARY ANSWER
Note that you DO NOT need to use keys/tokens using the Drive rest API with Android like you may find from other solutions (it may not hurt either, but it can). It doesn't match up here to what people talk about elsewhere (here they don't know what they're talking about).
See my notes here for further info: Google Drive via OAuth release version receives dailyLimitExceededUnreg
IF you have the problem however in debug build then you did not do everything correctly. My notes should get you on the right track.
If you need further help, I might assist because of how nuts it is.
EXAMPLE OF GETTING A FILE FROM DRIVE WITH THE NEW API USE
Just see from the following link
public Task<Pair<String, String>> readFile(String fileId)
https://github.com/gsuitedevs/android-samples/blob/master/drive/deprecation/app/src/main/java/com/google/android/gms/drive/sample/driveapimigration/DriveServiceHelper.java
The id comes from the query result where the id is part of that query info attached to that which is returned from the Drive query. The id is the file id for the file you want to retrieve. Pass that in to readFile and it gives you the file contents back that you can save locally to java.io.File eg fileOutputStream.write(contents.getBytes()); the contents of which is pair.second. You would then have your hopefully (because sometimes we have more work to do) identical java.io.File.
You can see a basic query if you need one in the the link sample as well but its missing some important info because depending on what you do you may need to check if trashed, get file size, modify time, md5, set order, etc. You may need to see https://developers.google.com/drive/api/v2/reference/files/list and https://developers.google.com/drive/api/v3/reference/files etc to figure that out. If enough files, there will be a paged requirement as well.
I know using Drive from code is a bit nuts (well, it is to me anyway lol) so hang in there =)
For Documentation purposes as a troubleshooting step:
Go to
https://console.developers.google.com/project/<project-id>/apiui/api
Or for Google Scripts:
https://script.google.com/home/usersettings
Replace with the ID of your application and check that the Google Drive API is turned on.
If it isn't - make sure to get a new token after turning it on if this is the case.
I have some code that has worked for months in production which suddenly has stopped working yesterday in all my apps.
I use Google Fit to retrieve locations via the Fitness.SensorClient API.
Locations permissions are requested correctly to the user at installation time (both the generic Android FINE_LOCATION permission and the permission to read and store locations into Google Fit).
I create the Fitness Option in this way:
setFitnessOptions(
FitnessOptions.builder()
.addDataType(DataType.TYPE_LOCATION_SAMPLE, FitnessOptions.ACCESS_WRITE));
I then look for the DataSources in this way:
GoogleSignInAccount lastSignedAccount = GoogleSignIn.getAccountForExtension(context, getFitnessOptions());
if (lastSignedAccount != null) {
Fitness.getSensorsClient(context, lastSignedAccount)
.findDataSources(
new DataSourcesRequest.Builder()
.setDataTypes(DataType.TYPE_LOCATION_SAMPLE)
.setDataSourceTypes(DataSource.TYPE_RAW)
.build())
.addOnSuccessListener(
new OnSuccessListener<List<DataSource>>() {
#Override
public void onSuccess(List<DataSource> dataSources) {
for (DataSource dataSource : dataSources) {
Log.i(TAG, "Data source found: " + dataSource.toString());
Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());
...
The value of the parameter dataSources in OnSuccessListener is an empty list. If I try other data types .setDataTypes(DataType.TYPE_STEP_COUNT_DELTA), dataSources is not an empty list. However neither DataType.TYPE_LOCATION_SAMPLE nor DataType.TYPE_LOCATION_TRACK return any data source.
I have checked the release notes of google services and nothing relevant seems to have changed.
The code above seems to be equivalent to all the examples provided by Google, e.g. this one
Does anyone have an idea of why suddenly that code has stopped working? Thanks
UPDATE: I have verified that it does not work with Android 6, 7, 8, or 9.
I have found the solution. In another part of my code I was subscribing using GoogleSignIn.getLastSignedInAccount. Apparently this may cause a mismatch and the locations are not returned. Changing the subscription to GoogleSignIn.getAccountForExtension solved the issue