mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-10 13:05:05 +00:00
Add (incomplete) handling of Android Wear Assets
This commit is contained in:
parent
51b8d384a1
commit
375004891e
4 changed files with 158 additions and 28 deletions
|
@ -27,8 +27,12 @@ import com.google.android.gms.wearable.internal.DataItemParcelable;
|
|||
import org.microg.wearable.proto.AssetEntry;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
public class DataItemRecord {
|
||||
public DataItemInternal dataItem;
|
||||
public String source;
|
||||
|
@ -67,6 +71,30 @@ public class DataItemRecord {
|
|||
return parcelable;
|
||||
}
|
||||
|
||||
public SetDataItem toSetDataItem() {
|
||||
SetDataItem.Builder builder = new SetDataItem.Builder()
|
||||
.packageName(packageName)
|
||||
.signatureDigest(signatureDigest)
|
||||
.uri(dataItem.uri.toString())
|
||||
.seqId(seqId)
|
||||
.deleted(deleted)
|
||||
.lastModified(lastModified);
|
||||
if (source != null) builder.source(source);
|
||||
if (dataItem.data != null) builder.data(ByteString.of(dataItem.data));
|
||||
List<AssetEntry> protoAssets = new ArrayList<AssetEntry>();
|
||||
Map<String, Asset> assets = dataItem.getAssets();
|
||||
for (String key : assets.keySet()) {
|
||||
protoAssets.add(new AssetEntry.Builder()
|
||||
.key(key)
|
||||
.unknown3(4)
|
||||
.value(new org.microg.wearable.proto.Asset.Builder()
|
||||
.digest(assets.get(key).getDigest())
|
||||
.build()).build());
|
||||
}
|
||||
builder.assets(protoAssets);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static DataItemRecord fromCursor(Cursor cursor) {
|
||||
DataItemRecord record = new DataItemRecord();
|
||||
record.packageName = cursor.getString(1);
|
||||
|
@ -78,6 +106,15 @@ public class DataItemRecord {
|
|||
record.dataItem.data = cursor.getBlob(8);
|
||||
record.lastModified = cursor.getLong(9);
|
||||
record.assetsAreReady = cursor.getLong(10) > 0;
|
||||
if (cursor.getString(11) != null) {
|
||||
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
|
||||
while (cursor.moveToNext()) {
|
||||
if (cursor.getLong(5) == record.seqId) {
|
||||
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
|
||||
}
|
||||
}
|
||||
cursor.moveToPrevious();
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
|
@ -87,7 +124,7 @@ public class DataItemRecord {
|
|||
if (setDataItem.data != null) record.dataItem.data = setDataItem.data.toByteArray();
|
||||
if (setDataItem.assets != null) {
|
||||
for (AssetEntry asset : setDataItem.assets) {
|
||||
// TODO record.dataItem.addAsset(asset.key, asset.value)
|
||||
record.dataItem.addAsset(asset.key, Asset.createFromRef(asset.value.digest));
|
||||
}
|
||||
}
|
||||
record.source = setDataItem.source;
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.microg.gms.wearable;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||
|
@ -86,6 +87,13 @@ public class MessageHandler extends ServerMessageListener {
|
|||
@Override
|
||||
public void onSetAsset(SetAsset setAsset) {
|
||||
Log.d(TAG, "onSetAsset: " + setAsset);
|
||||
Asset asset;
|
||||
if (setAsset.data != null) {
|
||||
asset = Asset.createFromBytes(setAsset.data.toByteArray());
|
||||
} else {
|
||||
asset = Asset.createFromRef(setAsset.digest);
|
||||
}
|
||||
service.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,6 +157,7 @@ public class MessageHandler extends ServerMessageListener {
|
|||
@Override
|
||||
public void onFilePiece(FilePiece filePiece) {
|
||||
Log.d(TAG, "onFilePiece: " + filePiece);
|
||||
service.handleFilePiece(getConnection(), filePiece.fileName, filePiece.piece.toByteArray(), filePiece.finalPiece ? filePiece.digest : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -143,7 +143,7 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
|||
}
|
||||
|
||||
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||
Log.d(TAG, "updateRecord not implemented: " + record);
|
||||
Log.d(TAG, "updateRecord no: " + record);
|
||||
}
|
||||
|
||||
private String insertRecord(SQLiteDatabase db, DataItemRecord record) {
|
||||
|
@ -220,8 +220,24 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
|||
if (cursor.moveToFirst()) {
|
||||
res = cursor.getLong(0);
|
||||
}
|
||||
cursor.close();;
|
||||
cursor.close();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void putAsset(Asset asset, boolean dataPresent) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("digest", asset.getDigest());
|
||||
cv.put("dataPresent", dataPresent ? 1 : 0);
|
||||
cv.put("timestampMs", System.currentTimeMillis());
|
||||
getWritableDatabase().insert("assets", null, cv);
|
||||
}
|
||||
|
||||
public void allowAssetAccess(String digest, String packageName, String signatureDigest) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("assets_digest", digest);
|
||||
cv.put("appkeys_it", getAppKey(db, packageName, signatureDigest));
|
||||
db.insert("assetsacls", null, cv);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,17 +53,23 @@ import com.google.android.gms.wearable.internal.PutDataResponse;
|
|||
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
|
||||
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.wearable.WearableConnection;
|
||||
import org.microg.wearable.proto.AssetEntry;
|
||||
import org.microg.wearable.proto.AckAsset;
|
||||
import org.microg.wearable.proto.AppKey;
|
||||
import org.microg.wearable.proto.AppKeys;
|
||||
import org.microg.wearable.proto.FilePiece;
|
||||
import org.microg.wearable.proto.RootMessage;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
import org.microg.wearable.proto.SetAsset;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -129,12 +135,13 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
|||
for (Map.Entry<String, Asset> assetEntry : request.getAssets().entrySet()) {
|
||||
Asset asset = prepareAsset(packageName, assetEntry.getValue());
|
||||
if (asset != null) {
|
||||
nodeDatabase.putAsset(asset, true);
|
||||
dataItem.addAsset(assetEntry.getKey(), asset);
|
||||
}
|
||||
}
|
||||
dataItem.data = request.getData();
|
||||
DataItemParcelable parcelable = putDataItem(packageName,
|
||||
PackageUtils.firstSignatureDigest(context, packageName), getLocalNodeId(), dataItem).toParcelable();
|
||||
DataItemParcelable parcelable = putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName),
|
||||
getLocalNodeId(), dataItem).toParcelable();
|
||||
callbacks.onPutDataResponse(new PutDataResponse(0, parcelable));
|
||||
}
|
||||
|
||||
|
@ -156,6 +163,13 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
|||
}
|
||||
|
||||
private Asset prepareAsset(String packageName, Asset asset) {
|
||||
if (asset.getFd() != null && asset.data == null) {
|
||||
try {
|
||||
asset.data = Utils.readStreamToEnd(new FileInputStream(asset.getFd().getFileDescriptor()));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
if (asset.data != null) {
|
||||
String digest = calculateDigest(asset.data);
|
||||
File assetFile = createAssetFile(digest);
|
||||
|
@ -188,6 +202,12 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
|||
return new File(dir, digest + ".asset");
|
||||
}
|
||||
|
||||
private File createAssetReceiveTempFile(String name) {
|
||||
File dir = new File(context.getFilesDir(), "piece");
|
||||
dir.mkdirs();
|
||||
return new File(dir, name);
|
||||
}
|
||||
|
||||
private String calculateDigest(byte[] data) {
|
||||
try {
|
||||
return Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(data), Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
|
||||
|
@ -480,29 +500,13 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
|||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
DataItemRecord record = DataItemRecord.fromCursor(cursor);
|
||||
Log.d(TAG, "Sync over " + connection + ": " + record);
|
||||
SetDataItem.Builder builder = new SetDataItem.Builder()
|
||||
.packageName(record.packageName)
|
||||
.signatureDigest(record.signatureDigest)
|
||||
.uri(record.dataItem.uri.toString())
|
||||
.seqId(record.seqId)
|
||||
.deleted(record.deleted)
|
||||
.lastModified(record.lastModified);
|
||||
if (record.source != null) builder.source(record.source);
|
||||
if (record.dataItem.data != null) builder.data(ByteString.of(record.dataItem.data));
|
||||
List<AssetEntry> protoAssets = new ArrayList<AssetEntry>();
|
||||
Map<String, Asset> assets = record.dataItem.getAssets();
|
||||
for (String key : assets.keySet()) {
|
||||
protoAssets.add(new AssetEntry.Builder()
|
||||
.key(key)
|
||||
.unknown3(4)
|
||||
.value(new org.microg.wearable.proto.Asset.Builder()
|
||||
.digest(assets.get(key).getDigest())
|
||||
.build()).build());
|
||||
for (Asset asset : record.dataItem.getAssets().values()) {
|
||||
syncAssetToPeer(connection, record, asset);
|
||||
}
|
||||
builder.assets(protoAssets);
|
||||
Log.d(TAG, "Sync over " + connection + ": " + record);
|
||||
|
||||
try {
|
||||
connection.writeMessage(new RootMessage.Builder().setDataItem(builder.build()).build());
|
||||
connection.writeMessage(new RootMessage.Builder().setDataItem(record.toSetDataItem()).build());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
break;
|
||||
|
@ -513,7 +517,71 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
|||
Log.d(TAG, "-- Done syncing over " + connection + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
||||
}
|
||||
|
||||
private void syncAssetToPeer(WearableConnection connection, DataItemRecord record, Asset asset) {
|
||||
try {
|
||||
Log.d(TAG, "Sync over " + connection + ": " + asset);
|
||||
connection.writeMessage(new RootMessage.Builder().setAsset(
|
||||
new SetAsset.Builder()
|
||||
.digest(asset.getDigest())
|
||||
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
||||
.build()).unknown13(true).build());
|
||||
File assetFile = createAssetFile(asset.getDigest());
|
||||
String fileName = calculateDigest(assetFile.toString().getBytes());
|
||||
FileInputStream fis = new FileInputStream(assetFile);
|
||||
byte[] arr = new byte[12215];
|
||||
ByteString lastPiece = null;
|
||||
int c = 0;
|
||||
while ((c = fis.read(arr)) > 0) {
|
||||
if (lastPiece != null) {
|
||||
Log.d(TAG, "Sync over " + connection + ": Asset piece for fileName " + fileName + ": " + lastPiece);
|
||||
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
|
||||
}
|
||||
lastPiece = ByteString.of(arr, 0, c);
|
||||
}
|
||||
Log.d(TAG, "Sync over " + connection + ": Last asset piece for fileName " + fileName + ": " + lastPiece);
|
||||
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
|
||||
nodeDatabase.putAsset(asset, false);
|
||||
for (AppKey appKey : appKeys) {
|
||||
nodeDatabase.allowAssetAccess(asset.getDigest(), appKey.packageName, appKey.signatureDigest);
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentSeqId(String nodeId) {
|
||||
return nodeDatabase.getCurrentSeqId(nodeId);
|
||||
}
|
||||
|
||||
public void handleFilePiece(WearableConnection connection, String fileName, byte[] bytes, String finalPieceDigest) {
|
||||
File file = createAssetReceiveTempFile(fileName);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(file, true);
|
||||
fos.write(bytes);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
if (finalPieceDigest != null) {
|
||||
// This is a final piece. If digest matches we're so happy!
|
||||
try {
|
||||
String digest = calculateDigest(Utils.readStreamToEnd(new FileInputStream(file)));
|
||||
if (digest.equals(finalPieceDigest)) {
|
||||
if (file.renameTo(createAssetFile(digest))) {
|
||||
// TODO: Mark as stored in db
|
||||
connection.writeMessage(new RootMessage.Builder().ackAsset(new AckAsset(digest)).build());
|
||||
} else {
|
||||
Log.w(TAG, "Could not rename to target file name. delete=" + file.delete());
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Received digest does not match. delete=" + file.delete());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed working with temp file. delete=" + file.delete(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue