Remove calls to the Package Installer

Remove calls to the Package Installer in order to be compliant with
Google Play Store rules.

Change-Id: Iab89c09e815af14728f08682d02e4447077aed55
parent 67407991
...@@ -8,12 +8,14 @@ package com.google.appinventor.components.runtime; ...@@ -8,12 +8,14 @@ package com.google.appinventor.components.runtime;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.DhcpInfo; import android.net.DhcpInfo;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Build; import android.os.Build;
...@@ -38,7 +40,6 @@ import com.google.appinventor.components.runtime.Form; ...@@ -38,7 +40,6 @@ import com.google.appinventor.components.runtime.Form;
import com.google.appinventor.components.runtime.ReplForm; import com.google.appinventor.components.runtime.ReplForm;
import com.google.appinventor.components.runtime.util.AppInvHTTPD; import com.google.appinventor.components.runtime.util.AppInvHTTPD;
import com.google.appinventor.components.runtime.util.EclairUtil; import com.google.appinventor.components.runtime.util.EclairUtil;
import com.google.appinventor.components.runtime.util.PackageInstaller;
import com.google.appinventor.components.runtime.util.SdkLevel; import com.google.appinventor.components.runtime.util.SdkLevel;
import com.google.appinventor.components.runtime.util.WebRTCNativeMgr; import com.google.appinventor.components.runtime.util.WebRTCNativeMgr;
...@@ -237,9 +238,11 @@ public class PhoneStatus extends AndroidNonvisibleComponent implements Component ...@@ -237,9 +238,11 @@ public class PhoneStatus extends AndroidNonvisibleComponent implements Component
// t.start(); // t.start();
} }
@SimpleFunction(description = "Downloads the URL and installs it as an Android Package") @SimpleFunction(description = "Downloads the URL and installs it as an Android Package via the installed browser")
public void installURL(String url) { public void installURL(String url) {
PackageInstaller.doPackageInstall(form, url); Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW).setData(uri);
form.startActivity(intent);
} }
@SimpleFunction(description = "Really Exit the Application") @SimpleFunction(description = "Really Exit the Application")
......
...@@ -248,75 +248,6 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -248,75 +248,6 @@ public class AppInvHTTPD extends NanoHTTPD {
}); });
} }
return (res); return (res);
} else if (uri.equals("/_update") || uri.equals("/_install")) { // Install a package, including a new companion
String url = parms.getProperty("url", "");
String inMac = parms.getProperty("mac", "");
String compMac;
if (!url.equals("") && (hmacKey != null) && !inMac.equals("")) {
try {
SecretKeySpec key = new SecretKeySpec(hmacKey, "RAW");
Mac hmacSha1 = Mac.getInstance("HmacSHA1");
hmacSha1.init(key);
byte [] tmpMac = hmacSha1.doFinal(url.getBytes());
StringBuffer sb = new StringBuffer(tmpMac.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : tmpMac)
formatter.format("%02x", b);
compMac = sb.toString();
} catch (Exception e) {
Log.e(LOG_TAG, "Error verifying update", e);
form.dispatchErrorOccurredEvent(form, "AppInvHTTPD",
ErrorMessages.ERROR_REPL_SECURITY_ERROR, "Exception working on HMAC for update");
Response res = new Response(HTTP_OK, MIME_JSON, "{\"status\" : \"BAD\", \"message\" : \"Security Error: Exception processing MAC\"}");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return(res);
}
Log.d(LOG_TAG, "Incoming Mac (update) = " + inMac);
Log.d(LOG_TAG, "Computed Mac (update) = " + compMac);
if (!inMac.equals(compMac)) {
Log.e(LOG_TAG, "Hmac does not match");
form.dispatchErrorOccurredEvent(form, "AppInvHTTPD",
ErrorMessages.ERROR_REPL_SECURITY_ERROR, "Invalid HMAC (update)");
Response res = new Response(HTTP_OK, MIME_JSON, "{\"status\" : \"BAD\", \"message\" : \"Security Error: Invalid MAC\"}");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return(res);
}
doPackageUpdate(url);
Response res = new Response(HTTP_OK, MIME_JSON, "{\"status\" : \"OK\", \"message\" : \"Update Should Happen\"}");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return (res);
} else {
Response res = new Response(HTTP_OK, MIME_JSON, "{\"status\" : \"BAD\", \"message\" : \"Missing Parameters\"}");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return(res);
}
} else if (uri.equals("/_package")) { // Handle installing a package
Response res;
String packageapk = parms.getProperty("package", null);
if (packageapk == null) {
res = new Response(HTTP_OK, MIME_PLAINTEXT, "NOT OK"); // Should really return an error code, but we don't look at it yet
return (res);
}
Log.d(LOG_TAG, rootDir + "/" + packageapk);
doPackageUpdate("file:///" + rootDir + "/" + packageapk);
res = new Response(HTTP_OK, MIME_PLAINTEXT, "OK");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return (res);
} else if (uri.equals("/_extensions")) { } else if (uri.equals("/_extensions")) {
return processLoadExtensionsRequest(parms); return processLoadExtensionsRequest(parms);
} }
...@@ -342,7 +273,7 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -342,7 +273,7 @@ public class AppInvHTTPD extends NanoHTTPD {
parentFileTo.mkdirs(); parentFileTo.mkdirs();
} }
if (!fileFrom.renameTo(fileTo)) { // First try rename if (!fileFrom.renameTo(fileTo)) { // First try rename
copyFile(fileFrom, fileTo); error = copyFile(fileFrom, fileTo);
fileFrom.delete(); // Remove temp file fileFrom.delete(); // Remove temp file
} }
} else { } else {
...@@ -355,7 +286,7 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -355,7 +286,7 @@ public class AppInvHTTPD extends NanoHTTPD {
error = true; error = true;
} }
if (error) { if (error) {
Response res = new Response(HTTP_OK, MIME_PLAINTEXT, "NOTOK"); Response res = new Response(HTTP_INTERNALERROR, MIME_PLAINTEXT, "NOTOK");
res.addHeader("Access-Control-Allow-Origin", "*"); res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type"); res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT"); res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
...@@ -371,54 +302,10 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -371,54 +302,10 @@ public class AppInvHTTPD extends NanoHTTPD {
} }
} }
Enumeration e = header.propertyNames();
while ( e.hasMoreElements())
{
String value = (String)e.nextElement();
Log.d(LOG_TAG, " HDR: '" + value + "' = '" +
header.getProperty( value ) + "'" );
}
e = parms.propertyNames();
while ( e.hasMoreElements())
{
String value = (String)e.nextElement();
Log.d(LOG_TAG, " PRM: '" + value + "' = '" +
parms.getProperty( value ) + "'" );
}
e = files.propertyNames();
while ( e.hasMoreElements())
{
String fieldname = (String)e.nextElement();
String tempLocation = (String) files.getProperty(fieldname);
String filename = (String) parms.getProperty(fieldname);
if (filename.startsWith("..") || filename.endsWith("..")
|| filename.indexOf("../") >= 0) {
Log.d(LOG_TAG, " Ignoring invalid filename: " + filename);
filename = null;
}
File fileFrom = new File(tempLocation);
if (filename == null) {
fileFrom.delete(); // Cleanup our mess (remove temp file).
} else {
File fileTo = new File(rootDir + "/" + filename);
if (!fileFrom.renameTo(fileTo)) { // First try rename, otherwise we have to copy
copyFile(fileFrom, fileTo);
fileFrom.delete(); // Cleanup temp file
}
}
Log.d(LOG_TAG, " UPLOADED: '" + filename + "' was at '" + tempLocation + "'");
Response res = new Response(HTTP_OK, MIME_PLAINTEXT, "OK");
res.addHeader("Access-Control-Allow-Origin", "*");
res.addHeader("Access-Control-Allow-Headers", "origin, content-type");
res.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,GET,HEAD,PUT");
res.addHeader("Allow", "POST,OPTIONS,GET,HEAD,PUT");
return(res);
}
return serveFile( uri, header, rootDir, true ); return serveFile( uri, header, rootDir, true );
} }
private void copyFile(File infile, File outfile) { private boolean copyFile(File infile, File outfile) {
try { try {
FileInputStream in = new FileInputStream(infile); FileInputStream in = new FileInputStream(infile);
FileOutputStream out = new FileOutputStream(outfile); FileOutputStream out = new FileOutputStream(outfile);
...@@ -431,8 +318,10 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -431,8 +318,10 @@ public class AppInvHTTPD extends NanoHTTPD {
in.close(); in.close();
out.close(); out.close();
return false; // No Error
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return true; // Oops
} }
} }
...@@ -514,10 +403,6 @@ public class AppInvHTTPD extends NanoHTTPD { ...@@ -514,10 +403,6 @@ public class AppInvHTTPD extends NanoHTTPD {
seq = 1; // Initialize this now seq = 1; // Initialize this now
} }
private void doPackageUpdate(final String inurl) {
PackageInstaller.doPackageInstall(form, inurl);
}
public void resetSeq() { public void resetSeq() {
seq = 1; seq = 1;
} }
......
...@@ -84,27 +84,30 @@ public class AssetFetcher { ...@@ -84,27 +84,30 @@ public class AssetFetcher {
} }
public static void upgradeCompanion(final String cookieValue, final String inputUri) { public static void upgradeCompanion(final String cookieValue, final String inputUri) {
background.submit(new Runnable() { // The code below is commented out because of issues with the Google Play Store
@Override //
public void run() { // background.submit(new Runnable() {
String [] parts = inputUri.split("/", 0); // @Override
String asset = parts[parts.length-1]; // public void run() {
File assetFile = getFile(inputUri, cookieValue, asset, 0); // String [] parts = inputUri.split("/", 0);
if (assetFile != null) { // String asset = parts[parts.length-1];
try { // File assetFile = getFile(inputUri, cookieValue, asset, 0);
Form form = Form.getActiveForm(); // if (assetFile != null) {
Intent intent = new Intent(Intent.ACTION_VIEW); // try {
Uri packageuri = NougatUtil.getPackageUri(form, assetFile); // Form form = Form.getActiveForm();
intent.setDataAndType(packageuri, "application/vnd.android.package-archive"); // Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Uri packageuri = NougatUtil.getPackageUri(form, assetFile);
form.startActivity(intent); // intent.setDataAndType(packageuri, "application/vnd.android.package-archive");
} catch (Exception e) { // intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.e(LOG_TAG, "ERROR_UNABLE_TO_GET", e); // form.startActivity(intent);
RetValManager.sendError("Unable to Install new Companion Package."); // } catch (Exception e) {
} // Log.e(LOG_TAG, "ERROR_UNABLE_TO_GET", e);
} // RetValManager.sendError("Unable to Install new Companion Package.");
} // }
}); // }
// }
// });
return;
} }
public static void loadExtensions(String jsonString) { public static void loadExtensions(String jsonString) {
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2011-2019 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
// This work is licensed under a Creative Commons Attribution 3.0 Unported License.
package com.google.appinventor.components.runtime.util;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import com.google.appinventor.components.runtime.Form;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class PackageInstaller {
private static final String LOG_TAG = "PackageInstaller(AppInventor)";
private static final String REPL_ASSET_DIR =
Environment.getExternalStorageDirectory().getAbsolutePath() +
"/AppInventor/assets/";
// We don't instantiate this, we just have static methods
private PackageInstaller() {
}
public static void doPackageInstall(final Form form, final String inurl) {
AsynchUtil.runAsynchronously(new Runnable() {
@Override
public void run() {
Uri packageuri = null;
try {
URL url = new URL(inurl);
URLConnection conn = url.openConnection();
File rootDir = new File(REPL_ASSET_DIR);
InputStream instream = new BufferedInputStream(conn.getInputStream());
File apkfile = new File(rootDir + "/package.apk");
FileOutputStream apkOut = new FileOutputStream(apkfile);
byte[] buffer = new byte[32768];
int len;
while ((len = instream.read(buffer, 0, 32768)) > 0) {
apkOut.write(buffer, 0, len);
}
instream.close();
apkOut.close();
// Call Package Manager Here
Log.d(LOG_TAG, "About to Install package from " + inurl);
Intent intent = new Intent(Intent.ACTION_VIEW);
packageuri = NougatUtil.getPackageUri(form, apkfile);
intent.setDataAndType(packageuri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
form.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(LOG_TAG, "Unable to install package", e);
form.dispatchErrorOccurredEvent(form, "PackageInstaller",
ErrorMessages.ERROR_UNABLE_TO_INSTALL_PACKAGE, packageuri);
} catch (Exception e) {
Log.e(LOG_TAG, "ERROR_UNABLE_TO_GET", e);
form.dispatchErrorOccurredEvent(form, "PackageInstaller",
ErrorMessages.ERROR_WEB_UNABLE_TO_GET, inurl);
}
}
});
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment