I'm using the APK expansion file in my application with the APEZProvider. This works on every device except Huawei devices. If the user wants to open a video it always comes to a RuntimeException and the video can't be played.
It happens on all Huawei devices (Android version is 8.0).
Is this a known issue with Huawei devices and how can I solve this problem?
Luckily this code is open source, so you can debug it yourself. The source code is here.
The relevant section:
int len = projection.length;
intProjection = new int[len];
for (int i = 0; i < len; i++) {
if (projection[i].equals(FILEID)) {
intProjection[i] = FILEID_IDX;
} else if (projection[i].equals(FILENAME)) {
intProjection[i] = FILENAME_IDX;
} else if (projection[i].equals(ZIPFILE)) {
intProjection[i] = ZIPFILE_IDX;
} else if (projection[i].equals(MODIFICATION)) {
intProjection[i] = MOD_IDX;
} else if (projection[i].equals(CRC32)) {
intProjection[i] = CRC_IDX;
} else if (projection[i].equals(COMPRESSEDLEN)) {
intProjection[i] = COMPLEN_IDX;
} else if (projection[i].equals(UNCOMPRESSEDLEN)) {
intProjection[i] = UNCOMPLEN_IDX;
} else if (projection[i].equals(COMPRESSIONTYPE)) {
intProjection[i] = COMPTYPE_IDX;
} else {
throw new RuntimeException();
}
Interesting things about this. Firstly, the line numbers don't match your line numbers. Secondly, the package name doesn't match the package name. Are you using the latest version? The bug may already have been fixed. The update which changed the package name also says "Updated for Marshmallow" which if you are getting breaks might explain why you are getting them on recent phones.
I have an Android app that is communicating with and arduino board via bluetooth
All the commands are going backward and forwards fine, until I wanted to send a comand of the type
"aT?bb"
from the android app, however when I print it in ardunio I am getting
"aT%3F"
I am logging the command in android and it is formed correctly My quesion is does c++/Arduino handle '?' differently then normal chars?
here is my arduino code->
while(bluetooth.available())
{
char toSend = (char)bluetooth.read();
if(toSend != '\0'){
if (toSend == 'a'){ i=0 ;}
inMsg[i] = toSend;
i++;
}
}
if(i == 5 )
{
// mock sending queries
if(inMsg[2] == '?'){
if(inMsg[1] == 'T'){
bluetooth.write("ty1");Serial.println("");
}else if(inMsg[1] == 'x'){ //normal cycle
bluetooth.write("xx1");
}else if(inMsg[1] == 'X'){ Serial.println(""); //booter
bluetooth.write("XX0");
}else if(inMsg[1] == 'N'){Serial.println(""); //On time
bluetooth.write("on1");
}else if(inMsg[1] == 'F'){ Serial.println(""); //Off time
bluetooth.write("of30");
}else if(inMsg[1] == 'S'){ Serial.println(""); //Speed percent
bluetooth.write("sp30");
}
}
// write to console
for(int j = 0; j < 5; j++){
Serial.write(inMsg[j]);
}
// new line
if(i == 5){Serial.println("");}
i = 0; // reset buffer
}
aT%3F <- this is mal formed
aS133 <- all the other are as I sent them from android
aN169
aF192
aS200
aXXXX
aYYYY
ayYYY
axXXX
my Android Code
...
command = "aT?bb";
writeCommand(command);
...
private void writeCommand(String command)
{
for (BluetoothGattCharacteristic characteristic : characteristics)
{
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) >0)
{
try {
characteristic.setValue(URLEncoder.encode(command, "utf-8"));
gatt.writeCharacteristic(characteristic);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
As pointed out in the comments above it was the URLEncoder that was changing the String. I have now changed this method to
private void writeCommand(String command)
{
for (BluetoothGattCharacteristic characteristic : characteristics)
{
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) >0)
{
characteristic.setValue(command);
gatt.writeCharacteristic(characteristic);
}else{
Log.i(TAG,"non write able");
}
}
}
i use different savegames in my app. "coins" , "levels" , ...
It works fine but if a conflict detected then its wrong result.
/**
* Conflict resolution for when Snapshots are opened. Must be run in an AsyncTask or in a
* background thread,
*/
Snapshots.OpenSnapshotResult processSnapshotOpenResult(Snapshots.OpenSnapshotResult result, int retryCount) {
retryCount++;
int status = result.getStatus().getStatusCode();
Log.i(TAG, "Load Result for saveGame<" + savedGame.getName() + "> status: " + status);
if (status == GamesStatusCodes.STATUS_OK) {
return result;
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONTENTS_UNAVAILABLE) {
return result;
} else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT) {
saveResolveConflictGameData = true;
Log.i(TAG, "Konflikt aufgetreten");
Snapshots.OpenSnapshotResult resolveResult = null;
Snapshot snapshot = result.getSnapshot();
Snapshot conflictSnapshot = result.getConflictingSnapshot();
Snapshot mResolvedSnapshot = null;
mResolvedSnapshot = snapshot;
SnapshotMetadata s1Meta = snapshot.getMetadata();
SnapshotMetadata cMeta = conflictSnapshot.getMetadata();
// resolveConflict and get new merged Parser Object
//
Parser conflictParserTemp = savedGame.resolveConflict(snapshot, conflictSnapshot);
if ( conflictParserTemp == null) {
Log.e(TAG, "savedGame.resolveConflict(snapshot,conflictSnapshot) Error");
return result;
}
//
// wurde schon ein conflict behandelt ?
//
if ( conflictParser != null ) {
// merge previous Conflict with this conflict
conflictParser.merge(conflictParserTemp);
} else {
// set first conflict Parser
conflictParser = conflictParserTemp;
}
Log.i(TAG, String.format("Games.Snapshots.resolveConflict() Step %d", retryCount));
resolveResult =
Games.Snapshots.resolveConflict(
activity.mGoogleApiClient, result.getConflictId(), mResolvedSnapshot).await();
if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) {
// Recursively attempt again
return processSnapshotOpenResult(resolveResult, retryCount);
} else {
// Failed, log error and show Toast to the user
String message = "Could not resolve snapshot conflicts";
Log.e(TAG, message);
Toast.makeText(activity.getBaseContext(), message, Toast.LENGTH_LONG).show();
return resolveResult;
}
}
// Fail, return null.
return null;
}
The Error is that if I load savegame "coins" I become all conflicts from other savegames.
I see it here.
SnapshotMetadata s1Meta = snapshot.getMetadata();
SnapshotMetadata cMeta = conflictSnapshot.getMetadata();
The Snapshot for korrekt coins savegame show this:
SnapshotMetadataEntity{Game=GameEntity{ApplicationId=520840013521,
DisplayName=Crush me, PrimaryCategory=Simulation,
SecondaryCategory=null, Description=hallo, DeveloperName=steffen
höhmann, IconImageUri=null, IconImageUrl=null, HiResImageUri=null,
HiResImageUrl=null, FeaturedImageUri=null, FeaturedImageUrl=null,
PlayEnabledGame=true, InstanceInstalled=true,
InstancePackageName=cherry.de.wubbleburst, AchievementTotalCount=0,
LeaderboardCount=0, RealTimeMultiplayerEnabled=false,
TurnBasedMultiplayerEnabled=false, AreSnapshotsEnabled=true,
ThemeColor=00456B, HasGamepadSupport=false},
Owner=PlayerEntity{PlayerId=113260033482974102226,
DisplayName=shoehmi, HasDebugAccess=false, IconImageUri=null,
IconImageUrl=null, HiResImageUri=null, HiResImageUrl=null,
RetrievedTimestamp=1454003980807, Title=Anfänger,
LevelInfo=com.google.android.gms.games.PlayerLevelInfo#1e1b36},
SnapshotId=drive://113260033482974102226/520840013521/coins,
CoverImageUri=null, CoverImageUrl=null, CoverImageAspectRatio=0.0,
Description=null, LastModifiedTimestamp=1454004003382, PlayedTime=-1,
UniqueName=coins, ChangePending=true, ProgressValue=-1}
drive://113260033482974102226/520840013521/coins
and the snapshotData:
timestamp;coins#1453929273252;100#1453929280956;-70#230179;70
but he shows me savegame snaphot from "level" savegame as conflicted Snapshot:
levelId;points#1;3241#2;9634
and the Conflict Snapshot Metadata say it is a "coins" savegame:
SnapshotMetadataEntity{Game=GameEntity{ApplicationId=520840013521,
DisplayName=Crush me, PrimaryCategory=Simulation,
SecondaryCategory=null, Description=hallo, DeveloperName=steffen
höhmann, IconImageUri=null, IconImageUrl=null, HiResImageUri=null,
HiResImageUrl=null, FeaturedImageUri=null, FeaturedImageUrl=null,
PlayEnabledGame=true, InstanceInstalled=true,
InstancePackageName=cherry.de.wubbleburst, AchievementTotalCount=0,
LeaderboardCount=0, RealTimeMultiplayerEnabled=false,
TurnBasedMultiplayerEnabled=false, AreSnapshotsEnabled=true,
ThemeColor=00456B, HasGamepadSupport=false},
Owner=PlayerEntity{PlayerId=113260033482974102226,
DisplayName=shoehmi, HasDebugAccess=false, IconImageUri=null,
IconImageUrl=null, HiResImageUri=null, HiResImageUrl=null,
RetrievedTimestamp=1454003980807, Title=Anfänger,
LevelInfo=com.google.android.gms.games.PlayerLevelInfo#1e1b36},
SnapshotId=drive://113260033482974102226/520840013521/coins,
CoverImageUri=null, CoverImageUrl=null, CoverImageAspectRatio=0.0,
Description=null, LastModifiedTimestamp=1454004003382, PlayedTime=-1,
UniqueName=coins, ChangePending=true, ProgressValue=-1}
drive://113260033482974102226/520840013521/coins
Why only if conflict occured and without conflicts its running correct and
save / load correct??
Please Help me???
sorry for my english ;)
I'm trying to get 'Change Subscriptions' to work using the Drive API for Android, but been unsuccessful so far.
Here the simple use case:
2 android devices, both using the same google account
both subscribe to the same 'file of interest' in their drive folder
if the file 'changes', be it from a change performed by one of the two devices or any external source, all devices that subscribed to this file are notified
As far as I understand, this is exactly what 'Change Subscriptions' are supposed to do for me. I'm using play services revision 27.
The problem I have:
A 'file content change' (or some other file event) made locally on one device is never properly propagated to the all other devices that subscribed to the same file.
Does anyone know of any solutions to this issue, or can point my to what I'm doing wrong?
I've written some simple testcode (see below), that only needs a connected googleApiClient, here's what I tested:
1.
device 1 creates a new testfile calling testFileWriteNew() and adds a change subscription to this file using testFileAddAndRemoveSubscription(), the expected log output:
testfile.txt created, driveId=DriveId:CAESABi0AyDAu9XZhVMoAA== resourceId=null
onCompletion; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
STATUS_SUCCESS
added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
2.
device 2 adds a change subscription to the same file using testFileAddAndRemoveSubscription(), the expected log output:
added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
As expected, the driveId is different on both devices, but the resourceId is the same 0B-sshen4iTFAN0htekFYNExuSEU, so that same 'cloud' file is referenced
3.
If I update the file with some new data via testFileUpdate I get the following on device 1:
testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
and device 2:
testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
4.
Unfortunately, the 'change of content' in the onChange method of the service is only triggered locally. A changed done by device 1 never reaches device 2 and vice versa. If I update the file using device 2 I see the following log on device 2 coming from the service:
onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
contentChanged
onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
metadataChanged
but I never see the onChange method being triggered on device 1, if device 2 triggered a change, which I would expect.
Code:
private boolean testFileWriteNew() {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
DriveContentsResult contentsResult = Drive.DriveApi.newDriveContents(mGoogleApiClient).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
DriveContents originalContents = contentsResult.getDriveContents();
OutputStream os = originalContents.getOutputStream();
try {
os.write(String.valueOf(System.currentTimeMillis()).getBytes());
MetadataChangeSet originalMetadata = new MetadataChangeSet.Builder().setTitle("testfile.txt").setMimeType("text/plain").build();
// create the file in root
DriveFolder.DriveFileResult fileResult = folderRoot.createFile(mGoogleApiClient, originalMetadata, originalContents, new ExecutionOptions.Builder().setNotifyOnCompletion(true).build()).await();
if (!fileResult.getStatus().isSuccess()) {
return false;
}
// check 'locally created' file, not yet synced to drive
DriveResource.MetadataResult metadataResult = fileResult.getDriveFile().getMetadata(mGoogleApiClient).await();
if (!metadataResult.getStatus().isSuccess()) {
return false;
}
Log.d(TAG, "testfile.txt created, driveId=" + metadataResult.getMetadata().getDriveId().encodeToString() + " resourceId=" + metadataResult.getMetadata().getDriveId().getResourceId());
return true;
} catch (IOException ioe) {
return false;
}
}
private boolean testFileUpdate() {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
// find testfile
DriveId testFile = null;
MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
if (!folderFilesSyncFolder.getStatus().isSuccess()) {
return false;
} else {
MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
for(int i = 0; i < bufferMetaData.getCount(); ++i) {
final Metadata data = bufferMetaData.get(i);
if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
testFile = data.getDriveId();
break;
}
}
bufferMetaData.release();
}
if(testFile == null) {
return false;
}
// update testfile
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
DriveContentsResult driveContentsResult = file.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
if (!driveContentsResult.getStatus().isSuccess()) {
return false;
}
DriveContents originalContents = driveContentsResult.getDriveContents();
OutputStream os = originalContents.getOutputStream();
try {
os.write(String.valueOf(System.currentTimeMillis()).getBytes());
// commit changes
com.google.android.gms.common.api.Status status = originalContents.commit(mGoogleApiClient, null).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "testfile.txt updated, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
} catch (IOException ioe) {
return false;
}
}
private boolean testFileAddAndRemoveSubscription(boolean subscribe) {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
// find testfile
DriveId testFile = null;
MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
if (!folderFilesSyncFolder.getStatus().isSuccess()) {
return false;
} else {
MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
for(int i = 0; i < bufferMetaData.getCount(); ++i) {
final Metadata data = bufferMetaData.get(i);
if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
testFile = data.getDriveId();
break;
}
}
bufferMetaData.release();
}
if(testFile == null) {
return false;
}
// subscribe & unsubscribe
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
if(subscribe) {
com.google.android.gms.common.api.Status status = file.addChangeSubscription(mGoogleApiClient).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "added subscription to testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
} else {
com.google.android.gms.common.api.Status status = file.removeChangeSubscription(mGoogleApiClient).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "removed subscription from testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
}
}
And here the service class:
public class ChangeService extends DriveEventService {
// TAG
private static final String TAG = ChangeService.class.getSimpleName();
#Override
public void onChange(ChangeEvent event) {
final DriveId driveId = event.getDriveId();
Log.e(TAG, "onChange; driveId=" + driveId.encodeToString() + " resourceId=" + driveId.getResourceId());
if(event.hasContentChanged()) { Log.e(TAG, "contentChanged"); }
else if(event.hasMetadataChanged()) { Log.e(TAG, "metadataChanged"); }
else if(event.hasBeenDeleted()) { Log.e(TAG, "beenDeleted"); }
}
#Override
public void onCompletion(CompletionEvent event) {
final DriveId driveId = event.getDriveId();
Log.e(TAG, "onCompletion; driveId=" + driveId.encodeToString() + " resourceId=" + driveId.getResourceId());
switch (event.getStatus()) {
case CompletionEvent.STATUS_CONFLICT: Log.e(TAG, "STATUS_CONFLICT"); break;
case CompletionEvent.STATUS_FAILURE: Log.e(TAG, "STATUS_FAILURE"); break;
case CompletionEvent.STATUS_SUCCESS: Log.e(TAG, "STATUS_SUCCESS "); break;
case CompletionEvent.STATUS_CANCELED: Log.e(TAG, "STATUS_CANCELED "); break;
}
event.dismiss();
}
}
I believe, you are falling into the same trap as many of us did before. I too originally assumed that the 'DriveEventService' takes care of notifications between multiple devices running under the same account. I tried and failed miserably, see here (and notice the resounding silence - since April 2014). I was always getting events on a single device only. So, I actually realized that Change Events work only locally within the GooPlaySvcs instance.
This was more or less confirmed by a comment from Steve Bazyl in this unrelated answer (please read including the 'ORIGINAL POST' paragraph), confirming my theory that both 'Change Events' and 'Completion Events' are local (Completion Events report result of network action - like http response).
So to answer your question. after fighting this for awhile, I had to develop a different strategy:
1/ perform GDAA action (create, update)
2/ wait for a Completion Event indicating your mod has been promoted to the Drive
3/ broadcast GCM message that include ResourceId (not DriveId !) plus optional data (up to 4K) to the registered participants.
4/ 'Registered participants' react to the message and download updated metadata/content, resolving the conflicts.
This solution is from summer 2014 and there may be some other pre-packaged solutions from Google since. I'd be happy myself to hear from people who know if there is more elegant solution.
Quite frankly, I don't understand what is this and this for, if the Completion Events do not timely reflect (notify of) the update from another device.
Good Luck
I can use the code below with getEncryptionState to determine whether the phone is encrypted.
To proceed, I need to verify thet the user input password is correct or not.
So I tried the verifyEncryptionPassword(), but it doesn't work well: every time I call this method, I always get the same return value: 0.
That means it takes any string as a correct encryption password which is apparently wrong.
IMountService mountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
boolean isEncrypted;
try {
isEncrypted = (mountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE);
if (isEncrypted) {
int result = mountService.verifyEncryptionPassword(candidatePw);
if (result == 0) {
Log.d(TAG, "Pw verifies");
} else if (result != -2) {
Log.d(TAG, "Pw mismatch");
} else {
Log.e(TAG, "verified failed");
}
}
} catch (Exception e) {
}
How can you verify the encryption password?