summaryrefslogtreecommitdiff
path: root/platform/android/java/src/com
diff options
context:
space:
mode:
authorfhuya <fhuya@google.com>2019-09-02 17:31:51 -0700
committerfhuya <fhuya@google.com>2019-09-04 16:20:22 -0700
commit7fabfd402f235ebcf64cfde3b399b8b62b969243 (patch)
tree99bb4eacc7828bedae43316f7415091de9782922 /platform/android/java/src/com
parentba854bbc7bb0eae230299de4da8dfcb7caf74b69 (diff)
Split the Android platform java logic into an Android library module (`lib`) and an application module (`app`).
The application module `app` serves double duties of providing the prebuilt Godot binaries ('android_debug.apk', 'android_release.apk') and the Godot custom build template ('android_source.zip').
Diffstat (limited to 'platform/android/java/src/com')
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/Constants.java236
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java80
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java297
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java201
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java367
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderClient.java126
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderService.java83
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/IStub.java41
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java129
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java112
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java92
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java229
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java852
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java1346
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java510
-rw-r--r--platform/android/java/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java200
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/AESObfuscator.java110
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/APKExpansionPolicy.java414
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/DeviceLimiter.java47
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java389
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/LicenseCheckerCallback.java67
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/LicenseValidator.java231
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/NullDeviceLimiter.java32
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/Obfuscator.java48
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/Policy.java65
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java80
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/ResponseData.java81
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/ServerManagedPolicy.java300
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/StrictPolicy.java100
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/ValidationException.java33
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/util/Base64.java578
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/util/Base64DecoderException.java32
-rw-r--r--platform/android/java/src/com/google/android/vending/licensing/util/URIQueryDecoder.java60
33 files changed, 0 insertions, 7568 deletions
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/Constants.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/Constants.java
deleted file mode 100644
index 1dcc370d83..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/Constants.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import java.io.File;
-
-
-/**
- * Contains the internal constants that are used in the download manager.
- * As a general rule, modifying these constants should be done with care.
- */
-public class Constants {
- /** Tag used for debugging/logging */
- public static final String TAG = "LVLDL";
-
- /**
- * Expansion path where we store obb files
- */
- public static final String EXP_PATH = File.separator + "Android"
- + File.separator + "obb" + File.separator;
-
- /** The intent that gets sent when the service must wake up for a retry */
- public static final String ACTION_RETRY = "android.intent.action.DOWNLOAD_WAKEUP";
-
- /** the intent that gets sent when clicking a successful download */
- public static final String ACTION_OPEN = "android.intent.action.DOWNLOAD_OPEN";
-
- /** the intent that gets sent when clicking an incomplete/failed download */
- public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST";
-
- /** the intent that gets sent when deleting the notification of a completed download */
- public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE";
-
- /**
- * When a number has to be appended to the filename, this string is used to separate the
- * base filename from the sequence number
- */
- public static final String FILENAME_SEQUENCE_SEPARATOR = "-";
-
- /** The default user agent used for downloads */
- public static final String DEFAULT_USER_AGENT = "Android.LVLDM";
-
- /** The buffer size used to stream the data */
- public static final int BUFFER_SIZE = 4096;
-
- /** The minimum amount of progress that has to be done before the progress bar gets updated */
- public static final int MIN_PROGRESS_STEP = 4096;
-
- /** The minimum amount of time that has to elapse before the progress bar gets updated, in ms */
- public static final long MIN_PROGRESS_TIME = 1000;
-
- /** The maximum number of rows in the database (FIFO) */
- public static final int MAX_DOWNLOADS = 1000;
-
- /**
- * The number of times that the download manager will retry its network
- * operations when no progress is happening before it gives up.
- */
- public static final int MAX_RETRIES = 5;
-
- /**
- * The minimum amount of time that the download manager accepts for
- * a Retry-After response header with a parameter in delta-seconds.
- */
- public static final int MIN_RETRY_AFTER = 30; // 30s
-
- /**
- * The maximum amount of time that the download manager accepts for
- * a Retry-After response header with a parameter in delta-seconds.
- */
- public static final int MAX_RETRY_AFTER = 24 * 60 * 60; // 24h
-
- /**
- * The maximum number of redirects.
- */
- public static final int MAX_REDIRECTS = 5; // can't be more than 7.
-
- /**
- * The time between a failure and the first retry after an IOException.
- * Each subsequent retry grows exponentially, doubling each time.
- * The time is in seconds.
- */
- public static final int RETRY_FIRST_DELAY = 30;
-
- /** Enable separate connectivity logging */
- public static final boolean LOGX = true;
-
- /** Enable verbose logging */
- public static final boolean LOGV = false;
-
- /** Enable super-verbose logging */
- private static final boolean LOCAL_LOGVV = false;
- public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
-
- /**
- * This download has successfully completed.
- * Warning: there might be other status values that indicate success
- * in the future.
- * Use isSucccess() to capture the entire category.
- */
- public static final int STATUS_SUCCESS = 200;
-
- /**
- * This request couldn't be parsed. This is also used when processing
- * requests with unknown/unsupported URI schemes.
- */
- public static final int STATUS_BAD_REQUEST = 400;
-
- /**
- * This download can't be performed because the content type cannot be
- * handled.
- */
- public static final int STATUS_NOT_ACCEPTABLE = 406;
-
- /**
- * This download cannot be performed because the length cannot be
- * determined accurately. This is the code for the HTTP error "Length
- * Required", which is typically used when making requests that require
- * a content length but don't have one, and it is also used in the
- * client when a response is received whose length cannot be determined
- * accurately (therefore making it impossible to know when a download
- * completes).
- */
- public static final int STATUS_LENGTH_REQUIRED = 411;
-
- /**
- * This download was interrupted and cannot be resumed.
- * This is the code for the HTTP error "Precondition Failed", and it is
- * also used in situations where the client doesn't have an ETag at all.
- */
- public static final int STATUS_PRECONDITION_FAILED = 412;
-
- /**
- * The lowest-valued error status that is not an actual HTTP status code.
- */
- public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
-
- /**
- * The requested destination file already exists.
- */
- public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
-
- /**
- * Some possibly transient error occurred, but we can't resume the download.
- */
- public static final int STATUS_CANNOT_RESUME = 489;
-
- /**
- * This download was canceled
- */
- public static final int STATUS_CANCELED = 490;
-
- /**
- * This download has completed with an error.
- * Warning: there will be other status values that indicate errors in
- * the future. Use isStatusError() to capture the entire category.
- */
- public static final int STATUS_UNKNOWN_ERROR = 491;
-
- /**
- * This download couldn't be completed because of a storage issue.
- * Typically, that's because the filesystem is missing or full.
- * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
- * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
- */
- public static final int STATUS_FILE_ERROR = 492;
-
- /**
- * This download couldn't be completed because of an HTTP
- * redirect response that the download manager couldn't
- * handle.
- */
- public static final int STATUS_UNHANDLED_REDIRECT = 493;
-
- /**
- * This download couldn't be completed because of an
- * unspecified unhandled HTTP code.
- */
- public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
-
- /**
- * This download couldn't be completed because of an
- * error receiving or processing data at the HTTP level.
- */
- public static final int STATUS_HTTP_DATA_ERROR = 495;
-
- /**
- * This download couldn't be completed because of an
- * HttpException while setting up the request.
- */
- public static final int STATUS_HTTP_EXCEPTION = 496;
-
- /**
- * This download couldn't be completed because there were
- * too many redirects.
- */
- public static final int STATUS_TOO_MANY_REDIRECTS = 497;
-
- /**
- * This download couldn't be completed due to insufficient storage
- * space. Typically, this is because the SD card is full.
- */
- public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
-
- /**
- * This download couldn't be completed because no external storage
- * device was found. Typically, this is because the SD card is not
- * mounted.
- */
- public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
-
- /**
- * The wake duration to check to see if a download is possible.
- */
- public static final long WATCHDOG_WAKE_TIMER = 60*1000;
-
- /**
- * The wake duration to check to see if the process was killed.
- */
- public static final long ACTIVE_THREAD_WATCHDOG = 5*1000;
-
-} \ No newline at end of file
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java
deleted file mode 100644
index 9cb294d721..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-
-/**
- * This class contains progress information about the active download(s).
- *
- * When you build the Activity that initiates a download and tracks the
- * progress by implementing the {@link IDownloaderClient} interface, you'll
- * receive a DownloadProgressInfo object in each call to the {@link
- * IDownloaderClient#onDownloadProgress} method. This allows you to update
- * your activity's UI with information about the download progress, such
- * as the progress so far, time remaining and current speed.
- */
-public class DownloadProgressInfo implements Parcelable {
- public long mOverallTotal;
- public long mOverallProgress;
- public long mTimeRemaining; // time remaining
- public float mCurrentSpeed; // speed in KB/S
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel p, int i) {
- p.writeLong(mOverallTotal);
- p.writeLong(mOverallProgress);
- p.writeLong(mTimeRemaining);
- p.writeFloat(mCurrentSpeed);
- }
-
- public DownloadProgressInfo(Parcel p) {
- mOverallTotal = p.readLong();
- mOverallProgress = p.readLong();
- mTimeRemaining = p.readLong();
- mCurrentSpeed = p.readFloat();
- }
-
- public DownloadProgressInfo(long overallTotal, long overallProgress,
- long timeRemaining,
- float currentSpeed) {
- this.mOverallTotal = overallTotal;
- this.mOverallProgress = overallProgress;
- this.mTimeRemaining = timeRemaining;
- this.mCurrentSpeed = currentSpeed;
- }
-
- public static final Creator<DownloadProgressInfo> CREATOR = new Creator<DownloadProgressInfo>() {
- @Override
- public DownloadProgressInfo createFromParcel(Parcel parcel) {
- return new DownloadProgressInfo(parcel);
- }
-
- @Override
- public DownloadProgressInfo[] newArray(int i) {
- return new DownloadProgressInfo[i];
- }
- };
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
deleted file mode 100644
index 452c7d1483..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import com.google.android.vending.expansion.downloader.impl.DownloaderService;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-
-// -- GODOT start --
-import java.lang.ref.WeakReference;
-// -- GODOT end --
-
-
-/**
- * This class binds the service API to your application client. It contains the IDownloaderClient proxy,
- * which is used to call functions in your client as well as the Stub, which is used to call functions
- * in the client implementation of IDownloaderClient.
- *
- * <p>The IPC is implemented using an Android Messenger and a service Binder. The connect method
- * should be called whenever the client wants to bind to the service. It opens up a service connection
- * that ends up calling the onServiceConnected client API that passes the service messenger
- * in. If the client wants to be notified by the service, it is responsible for then passing its
- * messenger to the service in a separate call.
- *
- * <p>Critical methods are {@link #startDownloadServiceIfRequired} and {@link #CreateStub}.
- *
- * <p>When your application first starts, you should first check whether your app's expansion files are
- * already on the device. If not, you should then call {@link #startDownloadServiceIfRequired}, which
- * starts your {@link impl.DownloaderService} to download the expansion files if necessary. The method
- * returns a value indicating whether download is required or not.
- *
- * <p>If a download is required, {@link #startDownloadServiceIfRequired} begins the download through
- * the specified service and you should then call {@link #CreateStub} to instantiate a member {@link
- * IStub} object that you need in order to receive calls through your {@link IDownloaderClient}
- * interface.
- */
-public class DownloaderClientMarshaller {
- public static final int MSG_ONDOWNLOADSTATE_CHANGED = 10;
- public static final int MSG_ONDOWNLOADPROGRESS = 11;
- public static final int MSG_ONSERVICECONNECTED = 12;
-
- public static final String PARAM_NEW_STATE = "newState";
- public static final String PARAM_PROGRESS = "progress";
- public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
-
- public static final int NO_DOWNLOAD_REQUIRED = DownloaderService.NO_DOWNLOAD_REQUIRED;
- public static final int LVL_CHECK_REQUIRED = DownloaderService.LVL_CHECK_REQUIRED;
- public static final int DOWNLOAD_REQUIRED = DownloaderService.DOWNLOAD_REQUIRED;
-
- private static class Proxy implements IDownloaderClient {
- private Messenger mServiceMessenger;
-
- @Override
- public void onDownloadStateChanged(int newState) {
- Bundle params = new Bundle(1);
- params.putInt(PARAM_NEW_STATE, newState);
- send(MSG_ONDOWNLOADSTATE_CHANGED, params);
- }
-
- @Override
- public void onDownloadProgress(DownloadProgressInfo progress) {
- Bundle params = new Bundle(1);
- params.putParcelable(PARAM_PROGRESS, progress);
- send(MSG_ONDOWNLOADPROGRESS, params);
- }
-
- private void send(int method, Bundle params) {
- Message m = Message.obtain(null, method);
- m.setData(params);
- try {
- mServiceMessenger.send(m);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- public Proxy(Messenger msg) {
- mServiceMessenger = msg;
- }
-
- @Override
- public void onServiceConnected(Messenger m) {
- /**
- * This is never called through the proxy.
- */
- }
- }
-
- private static class Stub implements IStub {
- private IDownloaderClient mItf = null;
- private Class<?> mDownloaderServiceClass;
- private boolean mBound;
- private Messenger mServiceMessenger;
- private Context mContext;
- /**
- * Target we publish for clients to send messages to IncomingHandler.
- */
- // -- GODOT start --
- private final MessengerHandlerClient mMsgHandler = new MessengerHandlerClient(this);
- final Messenger mMessenger = new Messenger(mMsgHandler);
-
- private static class MessengerHandlerClient extends Handler {
- private final WeakReference<Stub> mDownloader;
- public MessengerHandlerClient(Stub downloader) {
- mDownloader = new WeakReference<>(downloader);
- }
-
- @Override
- public void handleMessage(Message msg) {
- Stub downloader = mDownloader.get();
- if (downloader != null) {
- downloader.handleMessage(msg);
- }
- }
- }
-
- private void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ONDOWNLOADPROGRESS:
- Bundle bun = msg.getData();
- if (null != mContext) {
- bun.setClassLoader(mContext.getClassLoader());
- DownloadProgressInfo dpi = (DownloadProgressInfo)msg.getData()
- .getParcelable(PARAM_PROGRESS);
- mItf.onDownloadProgress(dpi);
- }
- break;
- case MSG_ONDOWNLOADSTATE_CHANGED:
- mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
- break;
- case MSG_ONSERVICECONNECTED:
- mItf.onServiceConnected(
- (Messenger)msg.getData().getParcelable(PARAM_MESSENGER));
- break;
- }
- }
- // -- GODOT end --
-
- public Stub(IDownloaderClient itf, Class<?> downloaderService) {
- mItf = itf;
- mDownloaderServiceClass = downloaderService;
- }
-
- /**
- * Class for interacting with the main interface of the service.
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- // This is called when the connection with the service has been
- // established, giving us the object we can use to
- // interact with the service. We are communicating with the
- // service using a Messenger, so here we get a client-side
- // representation of that from the raw IBinder object.
- mServiceMessenger = new Messenger(service);
- mItf.onServiceConnected(
- mServiceMessenger);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- // This is called when the connection with the service has been
- // unexpectedly disconnected -- that is, its process crashed.
- mServiceMessenger = null;
- }
- };
-
- @Override
- public void connect(Context c) {
- mContext = c;
- Intent bindIntent = new Intent(c, mDownloaderServiceClass);
- bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
- if ( !c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND) ) {
- if ( Constants.LOGVV ) {
- Log.d(Constants.TAG, "Service Unbound");
- }
- } else {
- mBound = true;
- }
-
- }
-
- @Override
- public void disconnect(Context c) {
- if (mBound) {
- c.unbindService(mConnection);
- mBound = false;
- }
- mContext = null;
- }
-
- @Override
- public Messenger getMessenger() {
- return mMessenger;
- }
- }
-
- /**
- * Returns a proxy that will marshal calls to IDownloaderClient methods
- *
- * @param msg
- * @return
- */
- public static IDownloaderClient CreateProxy(Messenger msg) {
- return new Proxy(msg);
- }
-
- /**
- * Returns a stub object that, when connected, will listen for marshaled
- * {@link IDownloaderClient} methods and translate them into calls to the supplied
- * interface.
- *
- * @param itf An implementation of IDownloaderClient that will be called
- * when remote method calls are unmarshaled.
- * @param downloaderService The class for your implementation of {@link
- * impl.DownloaderService}.
- * @return The {@link IStub} that allows you to connect to the service such that
- * your {@link IDownloaderClient} receives status updates.
- */
- public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
- return new Stub(itf, downloaderService);
- }
-
- /**
- * Starts the download if necessary. This function starts a flow that does `
- * many things. 1) Checks to see if the APK version has been checked and
- * the metadata database updated 2) If the APK version does not match,
- * checks the new LVL status to see if a new download is required 3) If the
- * APK version does match, then checks to see if the download(s) have been
- * completed 4) If the downloads have been completed, returns
- * NO_DOWNLOAD_REQUIRED The idea is that this can be called during the
- * startup of an application to quickly ascertain if the application needs
- * to wait to hear about any updated APK expansion files. Note that this does
- * mean that the application MUST be run for the first time with a network
- * connection, even if Market delivers all of the files.
- *
- * @param context Your application Context.
- * @param notificationClient A PendingIntent to start the Activity in your application
- * that shows the download progress and which will also start the application when download
- * completes.
- * @param serviceClass the class of your {@link imp.DownloaderService} implementation
- * @return whether the service was started and the reason for starting the service.
- * Either {@link #NO_DOWNLOAD_REQUIRED}, {@link #LVL_CHECK_REQUIRED}, or {@link
- * #DOWNLOAD_REQUIRED}.
- * @throws NameNotFoundException
- */
- public static int startDownloadServiceIfRequired(Context context, PendingIntent notificationClient,
- Class<?> serviceClass)
- throws NameNotFoundException {
- return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
- serviceClass);
- }
-
- /**
- * This version assumes that the intent contains the pending intent as a parameter. This
- * is used for responding to alarms.
- * <p>The pending intent must be in an extra with the key {@link
- * impl.DownloaderService#EXTRA_PENDING_INTENT}.
- *
- * @param context
- * @param notificationClient
- * @param serviceClass the class of the service to start
- * @return
- * @throws NameNotFoundException
- */
- public static int startDownloadServiceIfRequired(Context context, Intent notificationClient,
- Class<?> serviceClass)
- throws NameNotFoundException {
- return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
- serviceClass);
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
deleted file mode 100644
index 3771d19c9b..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import com.google.android.vending.expansion.downloader.impl.DownloaderService;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-// -- GODOT start --
-import java.lang.ref.WeakReference;
-// -- GODOT end --
-
-
-/**
- * This class is used by the client activity to proxy requests to the Downloader
- * Service.
- *
- * Most importantly, you must call {@link #CreateProxy} during the {@link
- * IDownloaderClient#onServiceConnected} callback in your activity in order to instantiate
- * an {@link IDownloaderService} object that you can then use to issue commands to the {@link
- * DownloaderService} (such as to pause and resume downloads).
- */
-public class DownloaderServiceMarshaller {
-
- public static final int MSG_REQUEST_ABORT_DOWNLOAD =
- 1;
- public static final int MSG_REQUEST_PAUSE_DOWNLOAD =
- 2;
- public static final int MSG_SET_DOWNLOAD_FLAGS =
- 3;
- public static final int MSG_REQUEST_CONTINUE_DOWNLOAD =
- 4;
- public static final int MSG_REQUEST_DOWNLOAD_STATE =
- 5;
- public static final int MSG_REQUEST_CLIENT_UPDATE =
- 6;
-
- public static final String PARAMS_FLAGS = "flags";
- public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
-
- private static class Proxy implements IDownloaderService {
- private Messenger mMsg;
-
- private void send(int method, Bundle params) {
- Message m = Message.obtain(null, method);
- m.setData(params);
- try {
- mMsg.send(m);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- public Proxy(Messenger msg) {
- mMsg = msg;
- }
-
- @Override
- public void requestAbortDownload() {
- send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
- }
-
- @Override
- public void requestPauseDownload() {
- send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
- }
-
- @Override
- public void setDownloadFlags(int flags) {
- Bundle params = new Bundle();
- params.putInt(PARAMS_FLAGS, flags);
- send(MSG_SET_DOWNLOAD_FLAGS, params);
- }
-
- @Override
- public void requestContinueDownload() {
- send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
- }
-
- @Override
- public void requestDownloadStatus() {
- send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
- }
-
- @Override
- public void onClientUpdated(Messenger clientMessenger) {
- Bundle bundle = new Bundle(1);
- bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
- send(MSG_REQUEST_CLIENT_UPDATE, bundle);
- }
- }
-
- private static class Stub implements IStub {
- private IDownloaderService mItf = null;
- // -- GODOT start --
- private final MessengerHandlerServer mMsgHandler = new MessengerHandlerServer(this);
- final Messenger mMessenger = new Messenger(mMsgHandler);
-
- private static class MessengerHandlerServer extends Handler {
- private final WeakReference<Stub> mDownloader;
- public MessengerHandlerServer(Stub downloader) {
- mDownloader = new WeakReference<>(downloader);
- }
-
- @Override
- public void handleMessage(Message msg) {
- Stub downloader = mDownloader.get();
- if (downloader != null) {
- downloader.handleMessage(msg);
- }
- }
- }
-
- private void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REQUEST_ABORT_DOWNLOAD:
- mItf.requestAbortDownload();
- break;
- case MSG_REQUEST_CONTINUE_DOWNLOAD:
- mItf.requestContinueDownload();
- break;
- case MSG_REQUEST_PAUSE_DOWNLOAD:
- mItf.requestPauseDownload();
- break;
- case MSG_SET_DOWNLOAD_FLAGS:
- mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
- break;
- case MSG_REQUEST_DOWNLOAD_STATE:
- mItf.requestDownloadStatus();
- break;
- case MSG_REQUEST_CLIENT_UPDATE:
- mItf.onClientUpdated((Messenger)msg.getData().getParcelable(
- PARAM_MESSENGER));
- break;
- }
- }
- // -- GODOT end --
-
- public Stub(IDownloaderService itf) {
- mItf = itf;
- }
-
- @Override
- public Messenger getMessenger() {
- return mMessenger;
- }
-
- @Override
- public void connect(Context c) {
-
- }
-
- @Override
- public void disconnect(Context c) {
-
- }
- }
-
- /**
- * Returns a proxy that will marshall calls to IDownloaderService methods
- *
- * @param ctx
- * @return
- */
- public static IDownloaderService CreateProxy(Messenger msg) {
- return new Proxy(msg);
- }
-
- /**
- * Returns a stub object that, when connected, will listen for marshalled
- * IDownloaderService methods and translate them into calls to the supplied
- * interface.
- *
- * @param itf An implementation of IDownloaderService that will be called
- * when remote method calls are unmarshalled.
- * @return
- */
- public static IStub CreateStub(IDownloaderService itf) {
- return new Stub(itf);
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java
deleted file mode 100644
index 36cd6aacfe..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/Helpers.java
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.Environment;
-import android.os.StatFs;
-import android.os.SystemClock;
-import android.util.Log;
-
-// -- GODOT start --
-//import com.android.vending.expansion.downloader.R;
-import com.godot.game.R;
-// -- GODOT end --
-
-import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Random;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Some helper functions for the download manager
- */
-public class Helpers {
-
- public static Random sRandom = new Random(SystemClock.uptimeMillis());
-
- /** Regex used to parse content-disposition headers */
- private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern
- .compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\"");
-
- private Helpers() {
- }
-
- /*
- * Parse the Content-Disposition HTTP Header. The format of the header is defined here:
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
- * content that is going to be downloaded to the file system. We only support the attachment
- * type.
- */
- static String parseContentDisposition(String contentDisposition) {
- try {
- Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
- if (m.find()) {
- return m.group(1);
- }
- } catch (IllegalStateException ex) {
- // This function is defined as returning null when it can't parse
- // the header
- }
- return null;
- }
-
- /**
- * @return the root of the filesystem containing the given path
- */
- public static File getFilesystemRoot(String path) {
- File cache = Environment.getDownloadCacheDirectory();
- if (path.startsWith(cache.getPath())) {
- return cache;
- }
- File external = Environment.getExternalStorageDirectory();
- if (path.startsWith(external.getPath())) {
- return external;
- }
- throw new IllegalArgumentException(
- "Cannot determine filesystem root for " + path);
- }
-
- public static boolean isExternalMediaMounted() {
- if (!Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED)) {
- // No SD card found.
- if (Constants.LOGVV) {
- Log.d(Constants.TAG, "no external storage");
- }
- return false;
- }
- return true;
- }
-
- /**
- * @return the number of bytes available on the filesystem rooted at the given File
- */
- public static long getAvailableBytes(File root) {
- StatFs stat = new StatFs(root.getPath());
- // put a bit of margin (in case creating the file grows the system by a
- // few blocks)
- long availableBlocks = (long) stat.getAvailableBlocks() - 4;
- return stat.getBlockSize() * availableBlocks;
- }
-
- /**
- * Checks whether the filename looks legitimate
- */
- public static boolean isFilenameValid(String filename) {
- filename = filename.replaceFirst("/+", "/"); // normalize leading
- // slashes
- return filename.startsWith(Environment.getDownloadCacheDirectory().toString())
- || filename.startsWith(Environment.getExternalStorageDirectory().toString());
- }
-
- /*
- * Delete the given file from device
- */
- /* package */static void deleteFile(String path) {
- try {
- File file = new File(path);
- file.delete();
- } catch (Exception e) {
- Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
- }
- }
-
- /**
- * Showing progress in MB here. It would be nice to choose the unit (KB, MB, GB) based on total
- * file size, but given what we know about the expected ranges of file sizes for APK expansion
- * files, it's probably not necessary.
- *
- * @param overallProgress
- * @param overallTotal
- * @return
- */
-
- static public String getDownloadProgressString(long overallProgress, long overallTotal) {
- if (overallTotal == 0) {
- if (Constants.LOGVV) {
- Log.e(Constants.TAG, "Notification called when total is zero");
- }
- return "";
- }
- // -- GODOT start --
- return String.format(Locale.ENGLISH, "%.2f",
- (float) overallProgress / (1024.0f * 1024.0f))
- + "MB /" +
- String.format(Locale.ENGLISH, "%.2f", (float) overallTotal /
- (1024.0f * 1024.0f))
- + "MB";
- // -- GODOT end --
- }
-
- /**
- * Adds a percentile to getDownloadProgressString.
- *
- * @param overallProgress
- * @param overallTotal
- * @return
- */
- static public String getDownloadProgressStringNotification(long overallProgress,
- long overallTotal) {
- if (overallTotal == 0) {
- if (Constants.LOGVV) {
- Log.e(Constants.TAG, "Notification called when total is zero");
- }
- return "";
- }
- return getDownloadProgressString(overallProgress, overallTotal) + " (" +
- getDownloadProgressPercent(overallProgress, overallTotal) + ")";
- }
-
- public static String getDownloadProgressPercent(long overallProgress, long overallTotal) {
- if (overallTotal == 0) {
- if (Constants.LOGVV) {
- Log.e(Constants.TAG, "Notification called when total is zero");
- }
- return "";
- }
- return Long.toString(overallProgress * 100 / overallTotal) + "%";
- }
-
- public static String getSpeedString(float bytesPerMillisecond) {
- // -- GODOT start --
- return String.format(Locale.ENGLISH, "%.2f", bytesPerMillisecond * 1000 / 1024);
- // -- GODOT end --
- }
-
- public static String getTimeRemaining(long durationInMilliseconds) {
- SimpleDateFormat sdf;
- if (durationInMilliseconds > 1000 * 60 * 60) {
- sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
- } else {
- sdf = new SimpleDateFormat("mm:ss", Locale.getDefault());
- }
- return sdf.format(new Date(durationInMilliseconds - TimeZone.getDefault().getRawOffset()));
- }
-
- /**
- * Returns the file name (without full path) for an Expansion APK file from the given context.
- *
- * @param c the context
- * @param mainFile true for main file, false for patch file
- * @param versionCode the version of the file
- * @return String the file name of the expansion file
- */
- public static String getExpansionAPKFileName(Context c, boolean mainFile, int versionCode) {
- return (mainFile ? "main." : "patch.") + versionCode + "." + c.getPackageName() + ".obb";
- }
-
- /**
- * Returns the filename (where the file should be saved) from info about a download
- */
- static public String generateSaveFileName(Context c, String fileName) {
- String path = getSaveFilePath(c)
- + File.separator + fileName;
- return path;
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- static public String getSaveFilePath(Context c) {
- // This technically existed since Honeycomb, but it is critical
- // on KitKat and greater versions since it will create the
- // directory if needed
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- return c.getObbDir().toString();
- } else {
- File root = Environment.getExternalStorageDirectory();
- String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
- return path;
- }
- }
-
- /**
- * Helper function to ascertain the existence of a file and return true/false appropriately
- *
- * @param c the app/activity/service context
- * @param fileName the name (sans path) of the file to query
- * @param fileSize the size that the file must match
- * @param deleteFileOnMismatch if the file sizes do not match, delete the file
- * @return true if it does exist, false otherwise
- */
- static public boolean doesFileExist(Context c, String fileName, long fileSize,
- boolean deleteFileOnMismatch) {
- // the file may have been delivered by Play --- let's make sure
- // it's the size we expect
- File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
- if (fileForNewFile.exists()) {
- if (fileForNewFile.length() == fileSize) {
- return true;
- }
- if (deleteFileOnMismatch) {
- // delete the file --- we won't be able to resume
- // because we cannot confirm the integrity of the file
- fileForNewFile.delete();
- }
- }
- return false;
- }
-
- public static final int FS_READABLE = 0;
- public static final int FS_DOES_NOT_EXIST = 1;
- public static final int FS_CANNOT_READ = 2;
-
- /**
- * Helper function to ascertain whether a file can be read.
- *
- * @param c the app/activity/service context
- * @param fileName the name (sans path) of the file to query
- * @return true if it does exist, false otherwise
- */
- static public int getFileStatus(Context c, String fileName) {
- // the file may have been delivered by Play --- let's make sure
- // it's the size we expect
- File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
- int returnValue;
- if (fileForNewFile.exists()) {
- if (fileForNewFile.canRead()) {
- returnValue = FS_READABLE;
- } else {
- returnValue = FS_CANNOT_READ;
- }
- } else {
- returnValue = FS_DOES_NOT_EXIST;
- }
- return returnValue;
- }
-
- /**
- * Helper function to ascertain whether the application has the correct access to the OBB
- * directory to allow an OBB file to be written.
- *
- * @param c the app/activity/service context
- * @return true if the application can write an OBB file, false otherwise
- */
- static public boolean canWriteOBBFile(Context c) {
- String path = getSaveFilePath(c);
- File fileForNewFile = new File(path);
- boolean canWrite;
- if (fileForNewFile.exists()) {
- canWrite = fileForNewFile.isDirectory() && fileForNewFile.canWrite();
- } else {
- canWrite = fileForNewFile.mkdirs();
- }
- return canWrite;
- }
-
- /**
- * Converts download states that are returned by the
- * {@link IDownloaderClient#onDownloadStateChanged} callback into usable strings. This is useful
- * if using the state strings built into the library to display user messages.
- *
- * @param state One of the STATE_* constants from {@link IDownloaderClient}.
- * @return string resource ID for the corresponding string.
- */
- static public int getDownloaderStringResourceIDFromState(int state) {
- switch (state) {
- case IDownloaderClient.STATE_IDLE:
- return R.string.state_idle;
- case IDownloaderClient.STATE_FETCHING_URL:
- return R.string.state_fetching_url;
- case IDownloaderClient.STATE_CONNECTING:
- return R.string.state_connecting;
- case IDownloaderClient.STATE_DOWNLOADING:
- return R.string.state_downloading;
- case IDownloaderClient.STATE_COMPLETED:
- return R.string.state_completed;
- case IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE:
- return R.string.state_paused_network_unavailable;
- case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
- return R.string.state_paused_by_request;
- case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
- return R.string.state_paused_wifi_disabled;
- case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
- return R.string.state_paused_wifi_unavailable;
- case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED:
- return R.string.state_paused_wifi_disabled;
- case IDownloaderClient.STATE_PAUSED_NEED_WIFI:
- return R.string.state_paused_wifi_unavailable;
- case IDownloaderClient.STATE_PAUSED_ROAMING:
- return R.string.state_paused_roaming;
- case IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE:
- return R.string.state_paused_network_setup_failure;
- case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
- return R.string.state_paused_sdcard_unavailable;
- case IDownloaderClient.STATE_FAILED_UNLICENSED:
- return R.string.state_failed_unlicensed;
- case IDownloaderClient.STATE_FAILED_FETCHING_URL:
- return R.string.state_failed_fetching_url;
- case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
- return R.string.state_failed_sdcard_full;
- case IDownloaderClient.STATE_FAILED_CANCELED:
- return R.string.state_failed_cancelled;
- default:
- return R.string.state_unknown;
- }
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderClient.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderClient.java
deleted file mode 100644
index cef3794701..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderClient.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import android.os.Messenger;
-
-/**
- * This interface should be implemented by the client activity for the
- * downloader. It is used to pass status from the service to the client.
- */
-public interface IDownloaderClient {
- static final int STATE_IDLE = 1;
- static final int STATE_FETCHING_URL = 2;
- static final int STATE_CONNECTING = 3;
- static final int STATE_DOWNLOADING = 4;
- static final int STATE_COMPLETED = 5;
-
- static final int STATE_PAUSED_NETWORK_UNAVAILABLE = 6;
- static final int STATE_PAUSED_BY_REQUEST = 7;
-
- /**
- * Both STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION and
- * STATE_PAUSED_NEED_CELLULAR_PERMISSION imply that Wi-Fi is unavailable and
- * cellular permission will restart the service. Wi-Fi disabled means that
- * the Wi-Fi manager is returning that Wi-Fi is not enabled, while in the
- * other case Wi-Fi is enabled but not available.
- */
- static final int STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 8;
- static final int STATE_PAUSED_NEED_CELLULAR_PERMISSION = 9;
-
- /**
- * Both STATE_PAUSED_WIFI_DISABLED and STATE_PAUSED_NEED_WIFI imply that
- * Wi-Fi is unavailable and cellular permission will NOT restart the
- * service. Wi-Fi disabled means that the Wi-Fi manager is returning that
- * Wi-Fi is not enabled, while in the other case Wi-Fi is enabled but not
- * available.
- * <p>
- * The service does not return these values. We recommend that app
- * developers with very large payloads do not allow these payloads to be
- * downloaded over cellular connections.
- */
- static final int STATE_PAUSED_WIFI_DISABLED = 10;
- static final int STATE_PAUSED_NEED_WIFI = 11;
-
- static final int STATE_PAUSED_ROAMING = 12;
-
- /**
- * Scary case. We were on a network that redirected us to another website
- * that delivered us the wrong file.
- */
- static final int STATE_PAUSED_NETWORK_SETUP_FAILURE = 13;
-
- static final int STATE_PAUSED_SDCARD_UNAVAILABLE = 14;
-
- static final int STATE_FAILED_UNLICENSED = 15;
- static final int STATE_FAILED_FETCHING_URL = 16;
- static final int STATE_FAILED_SDCARD_FULL = 17;
- static final int STATE_FAILED_CANCELED = 18;
-
- static final int STATE_FAILED = 19;
-
- /**
- * Called internally by the stub when the service is bound to the client.
- * <p>
- * Critical implementation detail. In onServiceConnected we create the
- * remote service and marshaler. This is how we pass the client information
- * back to the service so the client can be properly notified of changes. We
- * must do this every time we reconnect to the service.
- * <p>
- * That is, when you receive this callback, you should call
- * {@link DownloaderServiceMarshaller#CreateProxy} to instantiate a member
- * instance of {@link IDownloaderService}, then call
- * {@link IDownloaderService#onClientUpdated} with the Messenger retrieved
- * from your {@link IStub} proxy object.
- *
- * @param m the service Messenger. This Messenger is used to call the
- * service API from the client.
- */
- void onServiceConnected(Messenger m);
-
- /**
- * Called when the download state changes. Depending on the state, there may
- * be user requests. The service is free to change the download state in the
- * middle of a user request, so the client should be able to handle this.
- * <p>
- * The Downloader Library includes a collection of string resources that
- * correspond to each of the states, which you can use to provide users a
- * useful message based on the state provided in this callback. To fetch the
- * appropriate string for a state, call
- * {@link Helpers#getDownloaderStringResourceIDFromState}.
- * <p>
- * What this means to the developer: The application has gotten a message
- * that the download has paused due to lack of WiFi. The developer should
- * then show UI asking the user if they want to enable downloading over
- * cellular connections with appropriate warnings. If the application
- * suddenly starts downloading, the application should revert to showing the
- * progress again, rather than leaving up the download over cellular UI up.
- *
- * @param newState one of the STATE_* values defined in IDownloaderClient
- */
- void onDownloadStateChanged(int newState);
-
- /**
- * Shows the download progress. This is intended to be used to fill out a
- * client UI. This progress should only be shown in a few states such as
- * STATE_DOWNLOADING.
- *
- * @param progress the DownloadProgressInfo object containing the current
- * progress of all downloads.
- */
- void onDownloadProgress(DownloadProgressInfo progress);
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderService.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderService.java
deleted file mode 100644
index 4de9de0c62..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/IDownloaderService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import com.google.android.vending.expansion.downloader.impl.DownloaderService;
-import android.os.Messenger;
-
-/**
- * This interface is implemented by the DownloaderService and by the
- * DownloaderServiceMarshaller. It contains functions to control the service.
- * When a client binds to the service, it must call the onClientUpdated
- * function.
- * <p>
- * You can acquire a proxy that implements this interface for your service by
- * calling {@link DownloaderServiceMarshaller#CreateProxy} during the
- * {@link IDownloaderClient#onServiceConnected} callback. At which point, you
- * should immediately call {@link #onClientUpdated}.
- */
-public interface IDownloaderService {
- /**
- * Set this flag in response to the
- * IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION state and then
- * call RequestContinueDownload to resume a download
- */
- public static final int FLAGS_DOWNLOAD_OVER_CELLULAR = 1;
-
- /**
- * Request that the service abort the current download. The service should
- * respond by changing the state to {@link IDownloaderClient.STATE_ABORTED}.
- */
- void requestAbortDownload();
-
- /**
- * Request that the service pause the current download. The service should
- * respond by changing the state to
- * {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
- */
- void requestPauseDownload();
-
- /**
- * Request that the service continue a paused download, when in any paused
- * or failed state, including
- * {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
- */
- void requestContinueDownload();
-
- /**
- * Set the flags for this download (e.g.
- * {@link DownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR}).
- *
- * @param flags
- */
- void setDownloadFlags(int flags);
-
- /**
- * Requests that the download status be sent to the client.
- */
- void requestDownloadStatus();
-
- /**
- * Call this when you get {@link
- * IDownloaderClient.onServiceConnected(Messenger m)} from the
- * DownloaderClient to register the client with the service. It will
- * automatically send the current status to the client.
- *
- * @param clientMessenger
- */
- void onClientUpdated(Messenger clientMessenger);
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/IStub.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/IStub.java
deleted file mode 100644
index d5bc3a843e..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/IStub.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import android.content.Context;
-import android.os.Messenger;
-
-/**
- * This is the interface that is used to connect/disconnect from the downloader
- * service.
- * <p>
- * You should get a proxy object that implements this interface by calling
- * {@link DownloaderClientMarshaller#CreateStub} in your activity when the
- * downloader service starts. Then, call {@link #connect} during your activity's
- * onResume() and call {@link #disconnect} during onStop().
- * <p>
- * Then during the {@link IDownloaderClient#onServiceConnected} callback, you
- * should call {@link #getMessenger} to pass the stub's Messenger object to
- * {@link IDownloaderService#onClientUpdated}.
- */
-public interface IStub {
- Messenger getMessenger();
-
- void connect(Context c);
-
- void disconnect(Context c);
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java
deleted file mode 100644
index a0e1165cc4..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/SystemFacade.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-// -- GODOT start --
-import android.annotation.SuppressLint;
-// -- GODOT end --
-
-/**
- * Contains useful helper functions, typically tied to the application context.
- */
-class SystemFacade {
- private Context mContext;
- private NotificationManager mNotificationManager;
-
- public SystemFacade(Context context) {
- mContext = context;
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- public long currentTimeMillis() {
- return System.currentTimeMillis();
- }
-
- public Integer getActiveNetworkType() {
- ConnectivityManager connectivity =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity == null) {
- Log.w(Constants.TAG, "couldn't get connectivity manager");
- return null;
- }
-
- @SuppressLint("MissingPermission")
- NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
- if (activeInfo == null) {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "network is not available");
- }
- return null;
- }
- return activeInfo.getType();
- }
-
- public boolean isNetworkRoaming() {
- ConnectivityManager connectivity =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity == null) {
- Log.w(Constants.TAG, "couldn't get connectivity manager");
- return false;
- }
-
- @SuppressLint("MissingPermission")
- NetworkInfo info = connectivity.getActiveNetworkInfo();
- boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
- TelephonyManager tm = (TelephonyManager) mContext
- .getSystemService(Context.TELEPHONY_SERVICE);
- if (null == tm) {
- Log.w(Constants.TAG, "couldn't get telephony manager");
- return false;
- }
- boolean isRoaming = isMobile && tm.isNetworkRoaming();
- if (Constants.LOGVV && isRoaming) {
- Log.v(Constants.TAG, "network is roaming");
- }
- return isRoaming;
- }
-
- public Long getMaxBytesOverMobile() {
- return (long) Integer.MAX_VALUE;
- }
-
- public Long getRecommendedMaxBytesOverMobile() {
- return 2097152L;
- }
-
- public void sendBroadcast(Intent intent) {
- mContext.sendBroadcast(intent);
- }
-
- public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
- return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
- }
-
- public void postNotification(long id, Notification notification) {
- /**
- * TODO: The system notification manager takes ints, not longs, as IDs,
- * but the download manager uses IDs take straight from the database,
- * which are longs. This will have to be dealt with at some point.
- */
- mNotificationManager.notify((int) id, notification);
- }
-
- public void cancelNotification(long id) {
- mNotificationManager.cancel((int) id);
- }
-
- public void cancelAllNotifications() {
- mNotificationManager.cancelAll();
- }
-
- public void startThread(Thread thread) {
- thread.start();
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java
deleted file mode 100644
index 3ccc191c60..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * This service differs from IntentService in a few minor ways/ It will not
- * auto-stop itself after the intent is handled unless the target returns "true"
- * in should stop. Since the goal of this service is to handle a single kind of
- * intent, it does not queue up batches of intents of the same type.
- */
-public abstract class CustomIntentService extends Service {
- private String mName;
- private boolean mRedelivery;
- private volatile ServiceHandler mServiceHandler;
- private volatile Looper mServiceLooper;
- private static final String LOG_TAG = "CustomIntentService";
- private static final int WHAT_MESSAGE = -10;
-
- public CustomIntentService(String paramString) {
- this.mName = paramString;
- }
-
- @Override
- public IBinder onBind(Intent paramIntent) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- HandlerThread localHandlerThread = new HandlerThread("IntentService["
- + this.mName + "]");
- localHandlerThread.start();
- this.mServiceLooper = localHandlerThread.getLooper();
- this.mServiceHandler = new ServiceHandler(this.mServiceLooper);
- }
-
- @Override
- public void onDestroy() {
- Thread localThread = this.mServiceLooper.getThread();
- if ((localThread != null) && (localThread.isAlive())) {
- localThread.interrupt();
- }
- this.mServiceLooper.quit();
- Log.d(LOG_TAG, "onDestroy");
- }
-
- protected abstract void onHandleIntent(Intent paramIntent);
-
- protected abstract boolean shouldStop();
-
- @Override
- public void onStart(Intent paramIntent, int startId) {
- if (!this.mServiceHandler.hasMessages(WHAT_MESSAGE)) {
- Message localMessage = this.mServiceHandler.obtainMessage();
- localMessage.arg1 = startId;
- localMessage.obj = paramIntent;
- localMessage.what = WHAT_MESSAGE;
- this.mServiceHandler.sendMessage(localMessage);
- }
- }
-
- @Override
- public int onStartCommand(Intent paramIntent, int flags, int startId) {
- onStart(paramIntent, startId);
- return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
- }
-
- public void setIntentRedelivery(boolean enabled) {
- this.mRedelivery = enabled;
- }
-
- private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message paramMessage) {
- CustomIntentService.this
- .onHandleIntent((Intent) paramMessage.obj);
- if (shouldStop()) {
- Log.d(LOG_TAG, "stopSelf");
- CustomIntentService.this.stopSelf(paramMessage.arg1);
- Log.d(LOG_TAG, "afterStopSelf");
- }
- }
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java
deleted file mode 100644
index 45111b16a3..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import com.google.android.vending.expansion.downloader.Constants;
-import com.google.android.vending.expansion.downloader.Helpers;
-
-import android.util.Log;
-
-/**
- * Representation of information about an individual download from the database.
- */
-public class DownloadInfo {
- public String mUri;
- public final int mIndex;
- public final String mFileName;
- public String mETag;
- public long mTotalBytes;
- public long mCurrentBytes;
- public long mLastMod;
- public int mStatus;
- public int mControl;
- public int mNumFailed;
- public int mRetryAfter;
- public int mRedirectCount;
-
- boolean mInitialized;
-
- public int mFuzz;
-
- public DownloadInfo(int index, String fileName, String pkg) {
- mFuzz = Helpers.sRandom.nextInt(1001);
- mFileName = fileName;
- mIndex = index;
- }
-
- public void resetDownload() {
- mCurrentBytes = 0;
- mETag = "";
- mLastMod = 0;
- mStatus = 0;
- mControl = 0;
- mNumFailed = 0;
- mRetryAfter = 0;
- mRedirectCount = 0;
- }
-
- /**
- * Returns the time when a download should be restarted.
- */
- public long restartTime(long now) {
- if (mNumFailed == 0) {
- return now;
- }
- if (mRetryAfter > 0) {
- return mLastMod + mRetryAfter;
- }
- return mLastMod +
- Constants.RETRY_FIRST_DELAY *
- (1000 + mFuzz) * (1 << (mNumFailed - 1));
- }
-
- public void logVerboseInfo() {
- Log.v(Constants.TAG, "Service adding new entry");
- Log.v(Constants.TAG, "FILENAME: " + mFileName);
- Log.v(Constants.TAG, "URI : " + mUri);
- Log.v(Constants.TAG, "FILENAME: " + mFileName);
- Log.v(Constants.TAG, "CONTROL : " + mControl);
- Log.v(Constants.TAG, "STATUS : " + mStatus);
- Log.v(Constants.TAG, "FAILED_C: " + mNumFailed);
- Log.v(Constants.TAG, "RETRY_AF: " + mRetryAfter);
- Log.v(Constants.TAG, "REDIRECT: " + mRedirectCount);
- Log.v(Constants.TAG, "LAST_MOD: " + mLastMod);
- Log.v(Constants.TAG, "TOTAL : " + mTotalBytes);
- Log.v(Constants.TAG, "CURRENT : " + mCurrentBytes);
- Log.v(Constants.TAG, "ETAG : " + mETag);
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
deleted file mode 100644
index 4b214b22d7..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-// -- GODOT start --
-//import com.android.vending.expansion.downloader.R;
-import com.godot.game.R;
-// -- GODOT end --
-
-import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
-import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
-import com.google.android.vending.expansion.downloader.Helpers;
-import com.google.android.vending.expansion.downloader.IDownloaderClient;
-
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.os.Build;
-import android.os.Messenger;
-import android.support.v4.app.NotificationCompat;
-
-/**
- * This class handles displaying the notification associated with the download
- * queue going on in the download manager. It handles multiple status types;
- * Some require user interaction and some do not. Some of the user interactions
- * may be transient. (for example: the user is queried to continue the download
- * on 3G when it started on WiFi, but then the phone locks onto WiFi again so
- * the prompt automatically goes away)
- * <p/>
- * The application interface for the downloader also needs to understand and
- * handle these transient states.
- */
-public class DownloadNotification implements IDownloaderClient {
-
- private int mState;
- private final Context mContext;
- private final NotificationManager mNotificationManager;
- private CharSequence mCurrentTitle;
-
- private IDownloaderClient mClientProxy;
- private NotificationCompat.Builder mActiveDownloadBuilder;
- private NotificationCompat.Builder mBuilder;
- private NotificationCompat.Builder mCurrentBuilder;
- private CharSequence mLabel;
- private String mCurrentText;
- private DownloadProgressInfo mProgressInfo;
- private PendingIntent mContentIntent;
-
- static final String LOGTAG = "DownloadNotification";
- static final int NOTIFICATION_ID = LOGTAG.hashCode();
-
- public PendingIntent getClientIntent() {
- return mContentIntent;
- }
-
- public void setClientIntent(PendingIntent clientIntent) {
- this.mBuilder.setContentIntent(clientIntent);
- this.mActiveDownloadBuilder.setContentIntent(clientIntent);
- this.mContentIntent = clientIntent;
- }
-
- public void resendState() {
- if (null != mClientProxy) {
- mClientProxy.onDownloadStateChanged(mState);
- }
- }
-
- @Override
- public void onDownloadStateChanged(int newState) {
- if (null != mClientProxy) {
- mClientProxy.onDownloadStateChanged(newState);
- }
- if (newState != mState) {
- mState = newState;
- if (newState == IDownloaderClient.STATE_IDLE || null == mContentIntent) {
- return;
- }
- int stringDownloadID;
- int iconResource;
- boolean ongoingEvent;
-
- // get the new title string and paused text
- switch (newState) {
- case 0:
- iconResource = android.R.drawable.stat_sys_warning;
- stringDownloadID = R.string.state_unknown;
- ongoingEvent = false;
- break;
-
- case IDownloaderClient.STATE_DOWNLOADING:
- iconResource = android.R.drawable.stat_sys_download;
- stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
- ongoingEvent = true;
- break;
-
- case IDownloaderClient.STATE_FETCHING_URL:
- case IDownloaderClient.STATE_CONNECTING:
- iconResource = android.R.drawable.stat_sys_download_done;
- stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
- ongoingEvent = true;
- break;
-
- case IDownloaderClient.STATE_COMPLETED:
- case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
- iconResource = android.R.drawable.stat_sys_download_done;
- stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
- ongoingEvent = false;
- break;
-
- case IDownloaderClient.STATE_FAILED:
- case IDownloaderClient.STATE_FAILED_CANCELED:
- case IDownloaderClient.STATE_FAILED_FETCHING_URL:
- case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
- case IDownloaderClient.STATE_FAILED_UNLICENSED:
- iconResource = android.R.drawable.stat_sys_warning;
- stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
- ongoingEvent = false;
- break;
-
- default:
- iconResource = android.R.drawable.stat_sys_warning;
- stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
- ongoingEvent = true;
- break;
- }
-
- mCurrentText = mContext.getString(stringDownloadID);
- mCurrentTitle = mLabel;
- mCurrentBuilder.setTicker(mLabel + ": " + mCurrentText);
- mCurrentBuilder.setSmallIcon(iconResource);
- mCurrentBuilder.setContentTitle(mCurrentTitle);
- mCurrentBuilder.setContentText(mCurrentText);
- if (ongoingEvent) {
- mCurrentBuilder.setOngoing(true);
- } else {
- mCurrentBuilder.setOngoing(false);
- mCurrentBuilder.setAutoCancel(true);
- }
- mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
- }
- }
-
- @Override
- public void onDownloadProgress(DownloadProgressInfo progress) {
- mProgressInfo = progress;
- if (null != mClientProxy) {
- mClientProxy.onDownloadProgress(progress);
- }
- if (progress.mOverallTotal <= 0) {
- // we just show the text
- mBuilder.setTicker(mCurrentTitle);
- mBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
- mBuilder.setContentTitle(mCurrentTitle);
- mBuilder.setContentText(mCurrentText);
- mCurrentBuilder = mBuilder;
- } else {
- mActiveDownloadBuilder.setProgress((int) progress.mOverallTotal, (int) progress.mOverallProgress, false);
- mActiveDownloadBuilder.setContentText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
- mActiveDownloadBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
- mActiveDownloadBuilder.setTicker(mLabel + ": " + mCurrentText);
- mActiveDownloadBuilder.setContentTitle(mLabel);
- mActiveDownloadBuilder.setContentInfo(mContext.getString(R.string.time_remaining_notification,
- Helpers.getTimeRemaining(progress.mTimeRemaining)));
- mCurrentBuilder = mActiveDownloadBuilder;
- }
- mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
- }
-
- /**
- * Called in response to onClientUpdated. Creates a new proxy and notifies
- * it of the current state.
- *
- * @param msg the client Messenger to notify
- */
- public void setMessenger(Messenger msg) {
- mClientProxy = DownloaderClientMarshaller.CreateProxy(msg);
- if (null != mProgressInfo) {
- mClientProxy.onDownloadProgress(mProgressInfo);
- }
- if (mState != -1) {
- mClientProxy.onDownloadStateChanged(mState);
- }
- }
-
- /**
- * Constructor
- *
- * @param ctx The context to use to obtain access to the Notification
- * Service
- */
- DownloadNotification(Context ctx, CharSequence applicationLabel) {
- mState = -1;
- mContext = ctx;
- mLabel = applicationLabel;
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- mActiveDownloadBuilder = new NotificationCompat.Builder(ctx);
- mBuilder = new NotificationCompat.Builder(ctx);
-
- // Set Notification category and priorities to something that makes sense for a long
- // lived background task.
- mActiveDownloadBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
- mActiveDownloadBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
-
- mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
- mBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
-
- mCurrentBuilder = mBuilder;
- }
-
- @Override
- public void onServiceConnected(Messenger m) {
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
deleted file mode 100644
index c114b8a64a..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
+++ /dev/null
@@ -1,852 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import com.google.android.vending.expansion.downloader.Constants;
-import com.google.android.vending.expansion.downloader.Helpers;
-import com.google.android.vending.expansion.downloader.IDownloaderClient;
-
-import android.content.Context;
-import android.os.PowerManager;
-import android.os.Process;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.SyncFailedException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Locale;
-
-/**
- * Runs an actual download
- */
-public class DownloadThread {
-
- private Context mContext;
- private DownloadInfo mInfo;
- private DownloaderService mService;
- private final DownloadsDB mDB;
- private final DownloadNotification mNotification;
- private String mUserAgent;
-
- public DownloadThread(DownloadInfo info, DownloaderService service,
- DownloadNotification notification) {
- mContext = service;
- mInfo = info;
- mService = service;
- mNotification = notification;
- mDB = DownloadsDB.getDB(service);
- mUserAgent = "APKXDL (Linux; U; Android " + android.os.Build.VERSION.RELEASE + ";"
- + Locale.getDefault().toString() + "; " + android.os.Build.DEVICE + "/"
- + android.os.Build.ID + ")" +
- service.getPackageName();
- }
-
- /**
- * Returns the default user agent
- */
- private String userAgent() {
- return mUserAgent;
- }
-
- /**
- * State for the entire run() method.
- */
- private static class State {
- public String mFilename;
- public FileOutputStream mStream;
- public boolean mCountRetry = false;
- public int mRetryAfter = 0;
- public int mRedirectCount = 0;
- public String mNewUri;
- public boolean mGotData = false;
- public String mRequestUri;
-
- public State(DownloadInfo info, DownloaderService service) {
- mRedirectCount = info.mRedirectCount;
- mRequestUri = info.mUri;
- mFilename = service.generateTempSaveFileName(info.mFileName);
- }
- }
-
- /**
- * State within executeDownload()
- */
- private static class InnerState {
- public int mBytesSoFar = 0;
- public int mBytesThisSession = 0;
- public String mHeaderETag;
- public boolean mContinuingDownload = false;
- public String mHeaderContentLength;
- public String mHeaderContentDisposition;
- public String mHeaderContentLocation;
- public int mBytesNotified = 0;
- public long mTimeLastNotification = 0;
- }
-
- /**
- * Raised from methods called by run() to indicate that the current request
- * should be stopped immediately. Note the message passed to this exception
- * will be logged and therefore must be guaranteed not to contain any PII,
- * meaning it generally can't include any information about the request URI,
- * headers, or destination filename.
- */
- private class StopRequest extends Throwable {
-
- private static final long serialVersionUID = 6338592678988347973L;
- public int mFinalStatus;
-
- public StopRequest(int finalStatus, String message) {
- super(message);
- mFinalStatus = finalStatus;
- }
-
- public StopRequest(int finalStatus, String message, Throwable throwable) {
- super(message, throwable);
- mFinalStatus = finalStatus;
- }
- }
-
- /**
- * Raised from methods called by executeDownload() to indicate that the
- * download should be retried immediately.
- */
- private class RetryDownload extends Throwable {
-
- private static final long serialVersionUID = 6196036036517540229L;
- }
-
- /**
- * Executes the download in a separate thread
- */
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- State state = new State(mInfo, mService);
- PowerManager.WakeLock wakeLock = null;
- int finalStatus = DownloaderService.STATUS_UNKNOWN_ERROR;
-
- try {
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- // -- GODOT start --
- //wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
- //wakeLock.acquire();
- wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "org.godot.game:wakelock");
- wakeLock.acquire(20 * 60 * 1000L /*20 minutes*/);
- // -- GODOT end --
-
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "initiating download for " + mInfo.mFileName);
- Log.v(Constants.TAG, " at " + mInfo.mUri);
- }
-
- boolean finished = false;
- while (!finished) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "initiating download for " + mInfo.mFileName);
- Log.v(Constants.TAG, " at " + mInfo.mUri);
- }
- // Set or unset proxy, which may have changed since last GET
- // request.
- // setDefaultProxy() supports null as proxy parameter.
- URL url = new URL(state.mRequestUri);
- HttpURLConnection request = (HttpURLConnection)url.openConnection();
- request.setRequestProperty("User-Agent", userAgent());
- try {
- executeDownload(state, request);
- finished = true;
- } catch (RetryDownload exc) {
- // fall through
- } finally {
- request.disconnect();
- request = null;
- }
- }
-
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "download completed for " + mInfo.mFileName);
- Log.v(Constants.TAG, " at " + mInfo.mUri);
- }
- finalizeDestinationFile(state);
- finalStatus = DownloaderService.STATUS_SUCCESS;
- } catch (StopRequest error) {
- // remove the cause before printing, in case it contains PII
- Log.w(Constants.TAG,
- "Aborting request for download " + mInfo.mFileName + ": " + error.getMessage());
- error.printStackTrace();
- finalStatus = error.mFinalStatus;
- // fall through to finally block
- } catch (Throwable ex) { // sometimes the socket code throws unchecked
- // exceptions
- Log.w(Constants.TAG, "Exception for " + mInfo.mFileName + ": " + ex);
- finalStatus = DownloaderService.STATUS_UNKNOWN_ERROR;
- // falls through to the code that reports an error
- } finally {
- if (wakeLock != null) {
- wakeLock.release();
- wakeLock = null;
- }
- cleanupDestination(state, finalStatus);
- notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter,
- state.mRedirectCount, state.mGotData, state.mFilename);
- }
- }
-
- /**
- * Fully execute a single download request - setup and send the request,
- * handle the response, and transfer the data to the destination file.
- */
- private void executeDownload(State state, HttpURLConnection request)
- throws StopRequest, RetryDownload {
- InnerState innerState = new InnerState();
- byte data[] = new byte[Constants.BUFFER_SIZE];
-
- checkPausedOrCanceled(state);
-
- setupDestinationFile(state, innerState);
- addRequestHeaders(innerState, request);
-
- // check just before sending the request to avoid using an invalid
- // connection at all
- checkConnectivity(state);
-
- mNotification.onDownloadStateChanged(IDownloaderClient.STATE_CONNECTING);
- int responseCode = sendRequest(state, request);
- handleExceptionalStatus(state, innerState, request, responseCode);
-
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "received response for " + mInfo.mUri);
- }
-
- processResponseHeaders(state, innerState, request);
- InputStream entityStream = openResponseEntity(state, request);
- mNotification.onDownloadStateChanged(IDownloaderClient.STATE_DOWNLOADING);
- transferData(state, innerState, data, entityStream);
- }
-
- /**
- * Check if current connectivity is valid for this request.
- */
- private void checkConnectivity(State state) throws StopRequest {
- switch (mService.getNetworkAvailabilityState(mDB)) {
- case DownloaderService.NETWORK_OK:
- return;
- case DownloaderService.NETWORK_NO_CONNECTION:
- throw new StopRequest(DownloaderService.STATUS_WAITING_FOR_NETWORK,
- "waiting for network to return");
- case DownloaderService.NETWORK_TYPE_DISALLOWED_BY_REQUESTOR:
- throw new StopRequest(
- DownloaderService.STATUS_QUEUED_FOR_WIFI_OR_CELLULAR_PERMISSION,
- "waiting for wifi or for download over cellular to be authorized");
- case DownloaderService.NETWORK_CANNOT_USE_ROAMING:
- throw new StopRequest(DownloaderService.STATUS_WAITING_FOR_NETWORK,
- "roaming is not allowed");
- case DownloaderService.NETWORK_UNUSABLE_DUE_TO_SIZE:
- throw new StopRequest(DownloaderService.STATUS_QUEUED_FOR_WIFI, "waiting for wifi");
- }
- }
-
- /**
- * Transfer as much data as possible from the HTTP response to the
- * destination file.
- *
- * @param data buffer to use to read data
- * @param entityStream stream for reading the HTTP response entity
- */
- private void transferData(State state, InnerState innerState, byte[] data,
- InputStream entityStream) throws StopRequest {
- for (;;) {
- int bytesRead = readFromResponse(state, innerState, data, entityStream);
- if (bytesRead == -1) { // success, end of stream already reached
- handleEndOfStream(state, innerState);
- return;
- }
-
- state.mGotData = true;
- writeDataToDestination(state, data, bytesRead);
- innerState.mBytesSoFar += bytesRead;
- innerState.mBytesThisSession += bytesRead;
- reportProgress(state, innerState);
-
- checkPausedOrCanceled(state);
- }
- }
-
- /**
- * Called after a successful completion to take any necessary action on the
- * downloaded file.
- */
- private void finalizeDestinationFile(State state) throws StopRequest {
- syncDestination(state);
- String tempFilename = state.mFilename;
- String finalFilename = Helpers.generateSaveFileName(mService, mInfo.mFileName);
- if (!state.mFilename.equals(finalFilename)) {
- File startFile = new File(tempFilename);
- File destFile = new File(finalFilename);
- if (mInfo.mTotalBytes != -1 && mInfo.mCurrentBytes == mInfo.mTotalBytes) {
- if (!startFile.renameTo(destFile)) {
- throw new StopRequest(DownloaderService.STATUS_FILE_ERROR,
- "unable to finalize destination file");
- }
- } else {
- throw new StopRequest(DownloaderService.STATUS_FILE_DELIVERED_INCORRECTLY,
- "file delivered with incorrect size. probably due to network not browser configured");
- }
- }
- }
-
- /**
- * Called just before the thread finishes, regardless of status, to take any
- * necessary action on the downloaded file.
- */
- private void cleanupDestination(State state, int finalStatus) {
- closeDestination(state);
- if (state.mFilename != null && DownloaderService.isStatusError(finalStatus)) {
- new File(state.mFilename).delete();
- state.mFilename = null;
- }
- }
-
- /**
- * Sync the destination file to storage.
- */
- private void syncDestination(State state) {
- FileOutputStream downloadedFileStream = null;
- try {
- downloadedFileStream = new FileOutputStream(state.mFilename, true);
- downloadedFileStream.getFD().sync();
- } catch (FileNotFoundException ex) {
- Log.w(Constants.TAG, "file " + state.mFilename + " not found: " + ex);
- } catch (SyncFailedException ex) {
- Log.w(Constants.TAG, "file " + state.mFilename + " sync failed: " + ex);
- } catch (IOException ex) {
- Log.w(Constants.TAG, "IOException trying to sync " + state.mFilename + ": " + ex);
- } catch (RuntimeException ex) {
- Log.w(Constants.TAG, "exception while syncing file: ", ex);
- } finally {
- if (downloadedFileStream != null) {
- try {
- downloadedFileStream.close();
- } catch (IOException ex) {
- Log.w(Constants.TAG, "IOException while closing synced file: ", ex);
- } catch (RuntimeException ex) {
- Log.w(Constants.TAG, "exception while closing file: ", ex);
- }
- }
- }
- }
-
- /**
- * Close the destination output stream.
- */
- private void closeDestination(State state) {
- try {
- // close the file
- if (state.mStream != null) {
- state.mStream.close();
- state.mStream = null;
- }
- } catch (IOException ex) {
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "exception when closing the file after download : " + ex);
- }
- // nothing can really be done if the file can't be closed
- }
- }
-
- /**
- * Check if the download has been paused or canceled, stopping the request
- * appropriately if it has been.
- */
- private void checkPausedOrCanceled(State state) throws StopRequest {
- if (mService.getControl() == DownloaderService.CONTROL_PAUSED) {
- int status = mService.getStatus();
- switch (status) {
- case DownloaderService.STATUS_PAUSED_BY_APP:
- throw new StopRequest(mService.getStatus(),
- "download paused");
- }
- }
- }
-
- /**
- * Report download progress through the database if necessary.
- */
- private void reportProgress(State state, InnerState innerState) {
- long now = System.currentTimeMillis();
- if (innerState.mBytesSoFar - innerState.mBytesNotified
- > Constants.MIN_PROGRESS_STEP
- && now - innerState.mTimeLastNotification
- > Constants.MIN_PROGRESS_TIME) {
- // we store progress updates to the database here
- mInfo.mCurrentBytes = innerState.mBytesSoFar;
- mDB.updateDownloadCurrentBytes(mInfo);
-
- innerState.mBytesNotified = innerState.mBytesSoFar;
- innerState.mTimeLastNotification = now;
-
- long totalBytesSoFar = innerState.mBytesThisSession + mService.mBytesSoFar;
-
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "downloaded " + mInfo.mCurrentBytes + " out of "
- + mInfo.mTotalBytes);
- Log.v(Constants.TAG, " total " + totalBytesSoFar + " out of "
- + mService.mTotalLength);
- }
-
- mService.notifyUpdateBytes(totalBytesSoFar);
- }
- }
-
- /**
- * Write a data buffer to the destination file.
- *
- * @param data buffer containing the data to write
- * @param bytesRead how many bytes to write from the buffer
- */
- private void writeDataToDestination(State state, byte[] data, int bytesRead)
- throws StopRequest {
- for (;;) {
- try {
- if (state.mStream == null) {
- state.mStream = new FileOutputStream(state.mFilename, true);
- }
- state.mStream.write(data, 0, bytesRead);
- // we close after every write --- this may be too inefficient
- closeDestination(state);
- return;
- } catch (IOException ex) {
- if (!Helpers.isExternalMediaMounted()) {
- throw new StopRequest(DownloaderService.STATUS_DEVICE_NOT_FOUND_ERROR,
- "external media not mounted while writing destination file");
- }
-
- long availableBytes =
- Helpers.getAvailableBytes(Helpers.getFilesystemRoot(state.mFilename));
- if (availableBytes < bytesRead) {
- throw new StopRequest(DownloaderService.STATUS_INSUFFICIENT_SPACE_ERROR,
- "insufficient space while writing destination file", ex);
- }
- throw new StopRequest(DownloaderService.STATUS_FILE_ERROR,
- "while writing destination file: " + ex.toString(), ex);
- }
- }
- }
-
- /**
- * Called when we've reached the end of the HTTP response stream, to update
- * the database and check for consistency.
- */
- private void handleEndOfStream(State state, InnerState innerState) throws StopRequest {
- mInfo.mCurrentBytes = innerState.mBytesSoFar;
- // this should always be set from the market
- // if ( innerState.mHeaderContentLength == null ) {
- // mInfo.mTotalBytes = innerState.mBytesSoFar;
- // }
- mDB.updateDownload(mInfo);
-
- boolean lengthMismatched = (innerState.mHeaderContentLength != null)
- && (innerState.mBytesSoFar != Integer.parseInt(innerState.mHeaderContentLength));
- if (lengthMismatched) {
- if (cannotResume(innerState)) {
- throw new StopRequest(DownloaderService.STATUS_CANNOT_RESUME,
- "mismatched content length");
- } else {
- throw new StopRequest(getFinalStatusForHttpError(state),
- "closed socket before end of file");
- }
- }
- }
-
- private boolean cannotResume(InnerState innerState) {
- return innerState.mBytesSoFar > 0 && innerState.mHeaderETag == null;
- }
-
- /**
- * Read some data from the HTTP response stream, handling I/O errors.
- *
- * @param data buffer to use to read data
- * @param entityStream stream for reading the HTTP response entity
- * @return the number of bytes actually read or -1 if the end of the stream
- * has been reached
- */
- private int readFromResponse(State state, InnerState innerState, byte[] data,
- InputStream entityStream) throws StopRequest {
- try {
- return entityStream.read(data);
- } catch (IOException ex) {
- logNetworkState();
- mInfo.mCurrentBytes = innerState.mBytesSoFar;
- mDB.updateDownload(mInfo);
- if (cannotResume(innerState)) {
- String message = "while reading response: " + ex.toString()
- + ", can't resume interrupted download with no ETag";
- throw new StopRequest(DownloaderService.STATUS_CANNOT_RESUME,
- message, ex);
- } else {
- throw new StopRequest(getFinalStatusForHttpError(state),
- "while reading response: " + ex.toString(), ex);
- }
- }
- }
-
- /**
- * Open a stream for the HTTP response entity, handling I/O errors.
- *
- * @return an InputStream to read the response entity
- */
- private InputStream openResponseEntity(State state, HttpURLConnection response)
- throws StopRequest {
- try {
- return response.getInputStream();
- } catch (IOException ex) {
- logNetworkState();
- throw new StopRequest(getFinalStatusForHttpError(state),
- "while getting entity: " + ex.toString(), ex);
- }
- }
-
- private void logNetworkState() {
- if (Constants.LOGX) {
- Log.i(Constants.TAG,
- "Net "
- + (mService.getNetworkAvailabilityState(mDB) == DownloaderService.NETWORK_OK ? "Up"
- : "Down"));
- }
- }
-
- /**
- * Read HTTP response headers and take appropriate action, including setting
- * up the destination file and updating the database.
- */
- private void processResponseHeaders(State state, InnerState innerState, HttpURLConnection response)
- throws StopRequest {
- if (innerState.mContinuingDownload) {
- // ignore response headers on resume requests
- return;
- }
-
- readResponseHeaders(state, innerState, response);
-
- try {
- state.mFilename = mService.generateSaveFile(mInfo.mFileName, mInfo.mTotalBytes);
- } catch (DownloaderService.GenerateSaveFileError exc) {
- throw new StopRequest(exc.mStatus, exc.mMessage);
- }
- try {
- state.mStream = new FileOutputStream(state.mFilename);
- } catch (FileNotFoundException exc) {
- // make sure the directory exists
- File pathFile = new File(Helpers.getSaveFilePath(mService));
- try {
- if (pathFile.mkdirs()) {
- state.mStream = new FileOutputStream(state.mFilename);
- }
- } catch (Exception ex) {
- throw new StopRequest(DownloaderService.STATUS_FILE_ERROR,
- "while opening destination file: " + exc.toString(), exc);
- }
- }
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "writing " + mInfo.mUri + " to " + state.mFilename);
- }
-
- updateDatabaseFromHeaders(state, innerState);
- // check connectivity again now that we know the total size
- checkConnectivity(state);
- }
-
- /**
- * Update necessary database fields based on values of HTTP response headers
- * that have been read.
- */
- private void updateDatabaseFromHeaders(State state, InnerState innerState) {
- mInfo.mETag = innerState.mHeaderETag;
- mDB.updateDownload(mInfo);
- }
-
- /**
- * Read headers from the HTTP response and store them into local state.
- */
- private void readResponseHeaders(State state, InnerState innerState, HttpURLConnection response)
- throws StopRequest {
- String value = response.getHeaderField("Content-Disposition");
- if (value != null) {
- innerState.mHeaderContentDisposition = value;
- }
- value = response.getHeaderField("Content-Location");
- if (value != null) {
- innerState.mHeaderContentLocation = value;
- }
- value = response.getHeaderField("ETag");
- if (value != null) {
- innerState.mHeaderETag = value;
- }
- String headerTransferEncoding = null;
- value = response.getHeaderField("Transfer-Encoding");
- if (value != null) {
- headerTransferEncoding = value;
- }
- String headerContentType = null;
- value = response.getHeaderField("Content-Type");
- if (value != null) {
- headerContentType = value;
- if (!headerContentType.equals("application/vnd.android.obb")) {
- throw new StopRequest(DownloaderService.STATUS_FILE_DELIVERED_INCORRECTLY,
- "file delivered with incorrect Mime type");
- }
- }
-
- if (headerTransferEncoding == null) {
- long contentLength = response.getContentLength();
- if (value != null) {
- // this is always set from Market
- if (contentLength != -1 && contentLength != mInfo.mTotalBytes) {
- // we're most likely on a bad wifi connection -- we should
- // probably
- // also look at the mime type --- but the size mismatch is
- // enough
- // to tell us that something is wrong here
- Log.e(Constants.TAG, "Incorrect file size delivered.");
- } else {
- innerState.mHeaderContentLength = Long.toString(contentLength);
- }
- }
- } else {
- // Ignore content-length with transfer-encoding - 2616 4.4 3
- if (Constants.LOGVV) {
- Log.v(Constants.TAG,
- "ignoring content-length because of xfer-encoding");
- }
- }
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Content-Disposition: " +
- innerState.mHeaderContentDisposition);
- Log.v(Constants.TAG, "Content-Length: " + innerState.mHeaderContentLength);
- Log.v(Constants.TAG, "Content-Location: " + innerState.mHeaderContentLocation);
- Log.v(Constants.TAG, "ETag: " + innerState.mHeaderETag);
- Log.v(Constants.TAG, "Transfer-Encoding: " + headerTransferEncoding);
- }
-
- boolean noSizeInfo = innerState.mHeaderContentLength == null
- && (headerTransferEncoding == null
- || !headerTransferEncoding.equalsIgnoreCase("chunked"));
- if (noSizeInfo) {
- throw new StopRequest(DownloaderService.STATUS_HTTP_DATA_ERROR,
- "can't know size of download, giving up");
- }
- }
-
- /**
- * Check the HTTP response status and handle anything unusual (e.g. not
- * 200/206).
- */
- private void handleExceptionalStatus(State state, InnerState innerState, HttpURLConnection connection, int responseCode)
- throws StopRequest, RetryDownload {
- if (responseCode == 503 && mInfo.mNumFailed < Constants.MAX_RETRIES) {
- handleServiceUnavailable(state, connection);
- }
- int expectedStatus = innerState.mContinuingDownload ? 206
- : DownloaderService.STATUS_SUCCESS;
- if (responseCode != expectedStatus) {
- handleOtherStatus(state, innerState, responseCode);
- } else {
- // no longer redirected
- state.mRedirectCount = 0;
- }
- }
-
- /**
- * Handle a status that we don't know how to deal with properly.
- */
- private void handleOtherStatus(State state, InnerState innerState, int statusCode)
- throws StopRequest {
- int finalStatus;
- if (DownloaderService.isStatusError(statusCode)) {
- finalStatus = statusCode;
- } else if (statusCode >= 300 && statusCode < 400) {
- finalStatus = DownloaderService.STATUS_UNHANDLED_REDIRECT;
- } else if (innerState.mContinuingDownload && statusCode == DownloaderService.STATUS_SUCCESS) {
- finalStatus = DownloaderService.STATUS_CANNOT_RESUME;
- } else {
- finalStatus = DownloaderService.STATUS_UNHANDLED_HTTP_CODE;
- }
- throw new StopRequest(finalStatus, "http error " + statusCode);
- }
-
- /**
- * Add headers for this download to the HTTP request to allow for resume.
- */
- private void addRequestHeaders(InnerState innerState, HttpURLConnection request) {
- if (innerState.mContinuingDownload) {
- if (innerState.mHeaderETag != null) {
- request.setRequestProperty("If-Match", innerState.mHeaderETag);
- }
- request.setRequestProperty("Range", "bytes=" + innerState.mBytesSoFar + "-");
- }
- }
-
- /**
- * Handle a 503 Service Unavailable status by processing the Retry-After
- * header.
- */
- private void handleServiceUnavailable(State state, HttpURLConnection connection) throws StopRequest {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "got HTTP response code 503");
- }
- state.mCountRetry = true;
- String retryAfterValue = connection.getHeaderField("Retry-After");
- if (retryAfterValue != null) {
- try {
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Retry-After :" + retryAfterValue);
- }
- state.mRetryAfter = Integer.parseInt(retryAfterValue);
- if (state.mRetryAfter < 0) {
- state.mRetryAfter = 0;
- } else {
- if (state.mRetryAfter < Constants.MIN_RETRY_AFTER) {
- state.mRetryAfter = Constants.MIN_RETRY_AFTER;
- } else if (state.mRetryAfter > Constants.MAX_RETRY_AFTER) {
- state.mRetryAfter = Constants.MAX_RETRY_AFTER;
- }
- state.mRetryAfter += Helpers.sRandom.nextInt(Constants.MIN_RETRY_AFTER + 1);
- state.mRetryAfter *= 1000;
- }
- } catch (NumberFormatException ex) {
- // ignored - retryAfter stays 0 in this case.
- }
- }
- throw new StopRequest(DownloaderService.STATUS_WAITING_TO_RETRY,
- "got 503 Service Unavailable, will retry later");
- }
-
- /**
- * Send the request to the server, handling any I/O exceptions.
- */
- private int sendRequest(State state, HttpURLConnection request)
- throws StopRequest {
- try {
- return request.getResponseCode();
- } catch (IllegalArgumentException ex) {
- throw new StopRequest(DownloaderService.STATUS_HTTP_DATA_ERROR,
- "while trying to execute request: " + ex.toString(), ex);
- } catch (IOException ex) {
- logNetworkState();
- throw new StopRequest(getFinalStatusForHttpError(state),
- "while trying to execute request: " + ex.toString(), ex);
- }
- }
-
- private int getFinalStatusForHttpError(State state) {
- if (mService.getNetworkAvailabilityState(mDB) != DownloaderService.NETWORK_OK) {
- return DownloaderService.STATUS_WAITING_FOR_NETWORK;
- } else if (mInfo.mNumFailed < Constants.MAX_RETRIES) {
- state.mCountRetry = true;
- return DownloaderService.STATUS_WAITING_TO_RETRY;
- } else {
- Log.w(Constants.TAG, "reached max retries for " + mInfo.mNumFailed);
- return DownloaderService.STATUS_HTTP_DATA_ERROR;
- }
- }
-
- /**
- * Prepare the destination file to receive data. If the file already exists,
- * we'll set up appropriately for resumption.
- */
- private void setupDestinationFile(State state, InnerState innerState)
- throws StopRequest {
- if (state.mFilename != null) { // only true if we've already run a
- // thread for this download
- if (!Helpers.isFilenameValid(state.mFilename)) {
- // this should never happen
- throw new StopRequest(DownloaderService.STATUS_FILE_ERROR,
- "found invalid internal destination filename");
- }
- // We're resuming a download that got interrupted
- File f = new File(state.mFilename);
- if (f.exists()) {
- long fileLength = f.length();
- if (fileLength == 0) {
- // The download hadn't actually started, we can restart from
- // scratch
- f.delete();
- state.mFilename = null;
- } else if (mInfo.mETag == null) {
- // This should've been caught upon failure
- f.delete();
- throw new StopRequest(DownloaderService.STATUS_CANNOT_RESUME,
- "Trying to resume a download that can't be resumed");
- } else {
- // All right, we'll be able to resume this download
- try {
- state.mStream = new FileOutputStream(state.mFilename, true);
- } catch (FileNotFoundException exc) {
- throw new StopRequest(DownloaderService.STATUS_FILE_ERROR,
- "while opening destination for resuming: " + exc.toString(), exc);
- }
- innerState.mBytesSoFar = (int) fileLength;
- if (mInfo.mTotalBytes != -1) {
- innerState.mHeaderContentLength = Long.toString(mInfo.mTotalBytes);
- }
- innerState.mHeaderETag = mInfo.mETag;
- innerState.mContinuingDownload = true;
- }
- }
- }
-
- if (state.mStream != null) {
- closeDestination(state);
- }
- }
-
- /**
- * Stores information about the completed download, and notifies the
- * initiating application.
- */
- private void notifyDownloadCompleted(
- int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData,
- String filename) {
- updateDownloadDatabase(
- status, countRetry, retryAfter, redirectCount, gotData, filename);
- if (DownloaderService.isStatusCompleted(status)) {
- // TBD: send status update?
- }
- }
-
- private void updateDownloadDatabase(
- int status, boolean countRetry, int retryAfter, int redirectCount, boolean gotData,
- String filename) {
- mInfo.mStatus = status;
- mInfo.mRetryAfter = retryAfter;
- mInfo.mRedirectCount = redirectCount;
- mInfo.mLastMod = System.currentTimeMillis();
- if (!countRetry) {
- mInfo.mNumFailed = 0;
- } else if (gotData) {
- mInfo.mNumFailed = 1;
- } else {
- mInfo.mNumFailed++;
- }
- mDB.updateDownload(mInfo);
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
deleted file mode 100644
index 8d41a76900..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
+++ /dev/null
@@ -1,1346 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import com.google.android.vending.expansion.downloader.Constants;
-import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
-import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
-import com.google.android.vending.expansion.downloader.Helpers;
-import com.google.android.vending.expansion.downloader.IDownloaderClient;
-import com.google.android.vending.expansion.downloader.IDownloaderService;
-import com.google.android.vending.expansion.downloader.IStub;
-import com.google.android.vending.licensing.AESObfuscator;
-import com.google.android.vending.licensing.APKExpansionPolicy;
-import com.google.android.vending.licensing.LicenseChecker;
-import com.google.android.vending.licensing.LicenseCheckerCallback;
-import com.google.android.vending.licensing.Policy;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Messenger;
-import android.os.SystemClock;
-import android.provider.Settings.Secure;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-// -- GODOT start --
-import android.annotation.SuppressLint;
-// -- GODOT end --
-
-import java.io.File;
-
-/**
- * Performs the background downloads requested by applications that use the
- * Downloads provider. This service does not run as a foreground task, so
- * Android may kill it off at will, but it will try to restart itself if it can.
- * Note that Android by default will kill off any process that has an open file
- * handle on the shared (SD Card) partition if the partition is unmounted.
- */
-public abstract class DownloaderService extends CustomIntentService implements IDownloaderService {
-
- public DownloaderService() {
- super("LVLDownloadService");
- }
-
- private static final String LOG_TAG = "LVLDL";
-
- // the following NETWORK_* constants are used to indicates specific reasons
- // for disallowing a
- // download from using a network, since specific causes can require special
- // handling
-
- /**
- * The network is usable for the given download.
- */
- public static final int NETWORK_OK = 1;
-
- /**
- * There is no network connectivity.
- */
- public static final int NETWORK_NO_CONNECTION = 2;
-
- /**
- * The download exceeds the maximum size for this network.
- */
- public static final int NETWORK_UNUSABLE_DUE_TO_SIZE = 3;
-
- /**
- * The download exceeds the recommended maximum size for this network, the
- * user must confirm for this download to proceed without WiFi.
- */
- public static final int NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE = 4;
-
- /**
- * The current connection is roaming, and the download can't proceed over a
- * roaming connection.
- */
- public static final int NETWORK_CANNOT_USE_ROAMING = 5;
-
- /**
- * The app requesting the download specific that it can't use the current
- * network connection.
- */
- public static final int NETWORK_TYPE_DISALLOWED_BY_REQUESTOR = 6;
-
- /**
- * For intents used to notify the user that a download exceeds a size
- * threshold, if this extra is true, WiFi is required for this download
- * size; otherwise, it is only recommended.
- */
- public static final String EXTRA_IS_WIFI_REQUIRED = "isWifiRequired";
- public static final String EXTRA_FILE_NAME = "downloadId";
-
- /**
- * Used with DOWNLOAD_STATUS
- */
- public static final String EXTRA_STATUS_STATE = "ESS";
- public static final String EXTRA_STATUS_TOTAL_SIZE = "ETS";
- public static final String EXTRA_STATUS_CURRENT_FILE_SIZE = "CFS";
- public static final String EXTRA_STATUS_TOTAL_PROGRESS = "TFP";
- public static final String EXTRA_STATUS_CURRENT_PROGRESS = "CFP";
-
- public static final String ACTION_DOWNLOADS_CHANGED = "downloadsChanged";
-
- /**
- * Broadcast intent action sent by the download manager when a download
- * completes.
- */
- public final static String ACTION_DOWNLOAD_COMPLETE = "lvldownloader.intent.action.DOWNLOAD_COMPLETE";
-
- /**
- * Broadcast intent action sent by the download manager when download status
- * changes.
- */
- public final static String ACTION_DOWNLOAD_STATUS = "lvldownloader.intent.action.DOWNLOAD_STATUS";
-
- /*
- * Lists the states that the download manager can set on a download to
- * notify applications of the download progress. The codes follow the HTTP
- * families:<br> 1xx: informational<br> 2xx: success<br> 3xx: redirects (not
- * used by the download manager)<br> 4xx: client errors<br> 5xx: server
- * errors
- */
-
- /**
- * Returns whether the status is informational (i.e. 1xx).
- */
- public static boolean isStatusInformational(int status) {
- return (status >= 100 && status < 200);
- }
-
- /**
- * Returns whether the status is a success (i.e. 2xx).
- */
- public static boolean isStatusSuccess(int status) {
- return (status >= 200 && status < 300);
- }
-
- /**
- * Returns whether the status is an error (i.e. 4xx or 5xx).
- */
- public static boolean isStatusError(int status) {
- return (status >= 400 && status < 600);
- }
-
- /**
- * Returns whether the status is a client error (i.e. 4xx).
- */
- public static boolean isStatusClientError(int status) {
- return (status >= 400 && status < 500);
- }
-
- /**
- * Returns whether the status is a server error (i.e. 5xx).
- */
- public static boolean isStatusServerError(int status) {
- return (status >= 500 && status < 600);
- }
-
- /**
- * Returns whether the download has completed (either with success or
- * error).
- */
- public static boolean isStatusCompleted(int status) {
- return (status >= 200 && status < 300)
- || (status >= 400 && status < 600);
- }
-
- /**
- * This download hasn't stated yet
- */
- public static final int STATUS_PENDING = 190;
-
- /**
- * This download has started
- */
- public static final int STATUS_RUNNING = 192;
-
- /**
- * This download has been paused by the owning app.
- */
- public static final int STATUS_PAUSED_BY_APP = 193;
-
- /**
- * This download encountered some network error and is waiting before
- * retrying the request.
- */
- public static final int STATUS_WAITING_TO_RETRY = 194;
-
- /**
- * This download is waiting for network connectivity to proceed.
- */
- public static final int STATUS_WAITING_FOR_NETWORK = 195;
-
- /**
- * This download is waiting for a Wi-Fi connection to proceed or for
- * permission to download over cellular.
- */
- public static final int STATUS_QUEUED_FOR_WIFI_OR_CELLULAR_PERMISSION = 196;
-
- /**
- * This download is waiting for a Wi-Fi connection to proceed.
- */
- public static final int STATUS_QUEUED_FOR_WIFI = 197;
-
- /**
- * This download has successfully completed. Warning: there might be other
- * status values that indicate success in the future. Use isSucccess() to
- * capture the entire category.
- *
- * @hide
- */
- public static final int STATUS_SUCCESS = 200;
-
- /**
- * The requested URL is no longer available
- */
- public static final int STATUS_FORBIDDEN = 403;
-
- /**
- * The file was delivered incorrectly
- */
- public static final int STATUS_FILE_DELIVERED_INCORRECTLY = 487;
-
- /**
- * The requested destination file already exists.
- */
- public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
-
- /**
- * Some possibly transient error occurred, but we can't resume the download.
- */
- public static final int STATUS_CANNOT_RESUME = 489;
-
- /**
- * This download was canceled
- *
- * @hide
- */
- public static final int STATUS_CANCELED = 490;
-
- /**
- * This download has completed with an error. Warning: there will be other
- * status values that indicate errors in the future. Use isStatusError() to
- * capture the entire category.
- */
- public static final int STATUS_UNKNOWN_ERROR = 491;
-
- /**
- * This download couldn't be completed because of a storage issue.
- * Typically, that's because the filesystem is missing or full. Use the more
- * specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR} and
- * {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
- *
- * @hide
- */
- public static final int STATUS_FILE_ERROR = 492;
-
- /**
- * This download couldn't be completed because of an HTTP redirect response
- * that the download manager couldn't handle.
- *
- * @hide
- */
- public static final int STATUS_UNHANDLED_REDIRECT = 493;
-
- /**
- * This download couldn't be completed because of an unspecified unhandled
- * HTTP code.
- *
- * @hide
- */
- public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
-
- /**
- * This download couldn't be completed because of an error receiving or
- * processing data at the HTTP level.
- *
- * @hide
- */
- public static final int STATUS_HTTP_DATA_ERROR = 495;
-
- /**
- * This download couldn't be completed because of an HttpException while
- * setting up the request.
- *
- * @hide
- */
- public static final int STATUS_HTTP_EXCEPTION = 496;
-
- /**
- * This download couldn't be completed because there were too many
- * redirects.
- *
- * @hide
- */
- public static final int STATUS_TOO_MANY_REDIRECTS = 497;
-
- /**
- * This download couldn't be completed due to insufficient storage space.
- * Typically, this is because the SD card is full.
- *
- * @hide
- */
- public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
-
- /**
- * This download couldn't be completed because no external storage device
- * was found. Typically, this is because the SD card is not mounted.
- *
- * @hide
- */
- public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
-
- /**
- * This download is allowed to run.
- *
- * @hide
- */
- public static final int CONTROL_RUN = 0;
-
- /**
- * This download must pause at the first opportunity.
- *
- * @hide
- */
- public static final int CONTROL_PAUSED = 1;
-
- /**
- * This download is visible but only shows in the notifications while it's
- * in progress.
- *
- * @hide
- */
- public static final int VISIBILITY_VISIBLE = 0;
-
- /**
- * This download is visible and shows in the notifications while in progress
- * and after completion.
- *
- * @hide
- */
- public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
-
- /**
- * This download doesn't show in the UI or in the notifications.
- *
- * @hide
- */
- public static final int VISIBILITY_HIDDEN = 2;
-
- /**
- * Bit flag for setAllowedNetworkTypes corresponding to
- * {@link ConnectivityManager#TYPE_MOBILE}.
- */
- public static final int NETWORK_MOBILE = 1 << 0;
-
- /**
- * Bit flag for setAllowedNetworkTypes corresponding to
- * {@link ConnectivityManager#TYPE_WIFI}.
- */
- public static final int NETWORK_WIFI = 1 << 1;
-
- private final static String TEMP_EXT = ".tmp";
-
- /**
- * Service thread status
- */
- private static boolean sIsRunning;
-
- @Override
- public IBinder onBind(Intent paramIntent) {
- Log.d(Constants.TAG, "Service Bound");
- return this.mServiceMessenger.getBinder();
- }
-
- /**
- * Network state.
- */
- private boolean mIsConnected;
- private boolean mIsFailover;
- private boolean mIsCellularConnection;
- private boolean mIsRoaming;
- private boolean mIsAtLeast3G;
- private boolean mIsAtLeast4G;
- private boolean mStateChanged;
-
- /**
- * Download state
- */
- private int mControl;
- private int mStatus;
-
- public boolean isWiFi() {
- return mIsConnected && !mIsCellularConnection;
- }
-
- /**
- * Bindings to important services
- */
- private ConnectivityManager mConnectivityManager;
- private WifiManager mWifiManager;
-
- /**
- * Package we are downloading for (defaults to package of application)
- */
- private PackageInfo mPackageInfo;
-
- /**
- * Byte counts
- */
- long mBytesSoFar;
- long mTotalLength;
- int mFileCount;
-
- /**
- * Used for calculating time remaining and speed
- */
- long mBytesAtSample;
- long mMillisecondsAtSample;
- float mAverageDownloadSpeed;
-
- /**
- * Our binding to the network state broadcasts
- */
- private BroadcastReceiver mConnReceiver;
- final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
- final private Messenger mServiceMessenger = mServiceStub.getMessenger();
- private Messenger mClientMessenger;
- private DownloadNotification mNotification;
- private PendingIntent mPendingIntent;
- private PendingIntent mAlarmIntent;
-
- /**
- * Updates the network type based upon the type and subtype returned from
- * the connectivity manager. Subtype is only used for cellular signals.
- *
- * @param type
- * @param subType
- */
- private void updateNetworkType(int type, int subType) {
- switch (type) {
- case ConnectivityManager.TYPE_WIFI:
- case ConnectivityManager.TYPE_ETHERNET:
- case ConnectivityManager.TYPE_BLUETOOTH:
- mIsCellularConnection = false;
- mIsAtLeast3G = false;
- mIsAtLeast4G = false;
- break;
- case ConnectivityManager.TYPE_WIMAX:
- mIsCellularConnection = true;
- mIsAtLeast3G = true;
- mIsAtLeast4G = true;
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mIsCellularConnection = true;
- switch (subType) {
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- case TelephonyManager.NETWORK_TYPE_CDMA:
- case TelephonyManager.NETWORK_TYPE_EDGE:
- case TelephonyManager.NETWORK_TYPE_GPRS:
- case TelephonyManager.NETWORK_TYPE_IDEN:
- mIsAtLeast3G = false;
- mIsAtLeast4G = false;
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- case TelephonyManager.NETWORK_TYPE_EVDO_0:
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_UMTS:
- mIsAtLeast3G = true;
- mIsAtLeast4G = false;
- break;
- case TelephonyManager.NETWORK_TYPE_LTE: // 4G
- case TelephonyManager.NETWORK_TYPE_EHRPD: // 3G ++ interop
- // with 4G
- case TelephonyManager.NETWORK_TYPE_HSPAP: // 3G ++ but
- // marketed as
- // 4G
- mIsAtLeast3G = true;
- mIsAtLeast4G = true;
- break;
- default:
- mIsCellularConnection = false;
- mIsAtLeast3G = false;
- mIsAtLeast4G = false;
- }
- }
- }
-
- private void updateNetworkState(NetworkInfo info) {
- boolean isConnected = mIsConnected;
- boolean isFailover = mIsFailover;
- boolean isCellularConnection = mIsCellularConnection;
- boolean isRoaming = mIsRoaming;
- boolean isAtLeast3G = mIsAtLeast3G;
- if (null != info) {
- mIsRoaming = info.isRoaming();
- mIsFailover = info.isFailover();
- mIsConnected = info.isConnected();
- updateNetworkType(info.getType(), info.getSubtype());
- } else {
- mIsRoaming = false;
- mIsFailover = false;
- mIsConnected = false;
- updateNetworkType(-1, -1);
- }
- mStateChanged = (mStateChanged || isConnected != mIsConnected
- || isFailover != mIsFailover
- || isCellularConnection != mIsCellularConnection
- || isRoaming != mIsRoaming || isAtLeast3G != mIsAtLeast3G);
- if (Constants.LOGVV) {
- if (mStateChanged) {
- Log.v(LOG_TAG, "Network state changed: ");
- Log.v(LOG_TAG, "Starting State: " +
- (isConnected ? "Connected " : "Not Connected ") +
- (isCellularConnection ? "Cellular " : "WiFi ") +
- (isRoaming ? "Roaming " : "Local ") +
- (isAtLeast3G ? "3G+ " : "<3G "));
- Log.v(LOG_TAG, "Ending State: " +
- (mIsConnected ? "Connected " : "Not Connected ") +
- (mIsCellularConnection ? "Cellular " : "WiFi ") +
- (mIsRoaming ? "Roaming " : "Local ") +
- (mIsAtLeast3G ? "3G+ " : "<3G "));
-
- if (isServiceRunning()) {
- if (mIsRoaming) {
- mStatus = STATUS_WAITING_FOR_NETWORK;
- mControl = CONTROL_PAUSED;
- } else if (mIsCellularConnection) {
- DownloadsDB db = DownloadsDB.getDB(this);
- int flags = db.getFlags();
- if (0 == (flags & FLAGS_DOWNLOAD_OVER_CELLULAR)) {
- mStatus = STATUS_QUEUED_FOR_WIFI;
- mControl = CONTROL_PAUSED;
- }
- }
- }
-
- }
- }
- }
-
- /**
- * Polls the network state, setting the flags appropriately.
- */
- void pollNetworkState() {
- if (null == mConnectivityManager) {
- mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- }
- if (null == mWifiManager) {
- mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
- }
- if (mConnectivityManager == null) {
- Log.w(Constants.TAG,
- "couldn't get connectivity manager to poll network state");
- } else {
- @SuppressLint("MissingPermission")
- NetworkInfo activeInfo = mConnectivityManager
- .getActiveNetworkInfo();
- updateNetworkState(activeInfo);
- }
- }
-
- public static final int NO_DOWNLOAD_REQUIRED = 0;
- public static final int LVL_CHECK_REQUIRED = 1;
- public static final int DOWNLOAD_REQUIRED = 2;
-
- public static final String EXTRA_PACKAGE_NAME = "EPN";
- public static final String EXTRA_PENDING_INTENT = "EPI";
- public static final String EXTRA_MESSAGE_HANDLER = "EMH";
-
- /**
- * Returns true if the LVL check is required
- *
- * @param db a downloads DB synchronized with the latest state
- * @param pi the package info for the project
- * @return returns true if the filenames need to be returned
- */
- private static boolean isLVLCheckRequired(DownloadsDB db, PackageInfo pi) {
- // we need to update the LVL check and get a successful status to
- // proceed
- if (db.mVersionCode != pi.versionCode) {
- return true;
- }
- return false;
- }
-
- /**
- * Careful! Only use this internally.
- *
- * @return whether we think the service is running
- */
- private static synchronized boolean isServiceRunning() {
- return sIsRunning;
- }
-
- private static synchronized void setServiceRunning(boolean isRunning) {
- sIsRunning = isRunning;
- }
-
- public static int startDownloadServiceIfRequired(Context context,
- Intent intent, Class<?> serviceClass) throws NameNotFoundException {
- final PendingIntent pendingIntent = (PendingIntent) intent
- .getParcelableExtra(EXTRA_PENDING_INTENT);
- return startDownloadServiceIfRequired(context, pendingIntent,
- serviceClass);
- }
-
- public static int startDownloadServiceIfRequired(Context context,
- PendingIntent pendingIntent, Class<?> serviceClass)
- throws NameNotFoundException
- {
- String packageName = context.getPackageName();
- String className = serviceClass.getName();
-
- return startDownloadServiceIfRequired(context, pendingIntent,
- packageName, className);
- }
-
- /**
- * Starts the download if necessary. This function starts a flow that does `
- * many things. 1) Checks to see if the APK version has been checked and the
- * metadata database updated 2) If the APK version does not match, checks
- * the new LVL status to see if a new download is required 3) If the APK
- * version does match, then checks to see if the download(s) have been
- * completed 4) If the downloads have been completed, returns
- * NO_DOWNLOAD_REQUIRED The idea is that this can be called during the
- * startup of an application to quickly ascertain if the application needs
- * to wait to hear about any updated APK expansion files. Note that this
- * does mean that the application MUST be run for the first time with a
- * network connection, even if Market delivers all of the files.
- *
- * @param context
- * @param pendingIntent
- * @return true if the app should wait for more guidance from the
- * downloader, false if the app can continue
- * @throws NameNotFoundException
- */
- public static int startDownloadServiceIfRequired(Context context,
- PendingIntent pendingIntent, String classPackage, String className)
- throws NameNotFoundException {
- // first: do we need to do an LVL update?
- // we begin by getting our APK version from the package manager
- final PackageInfo pi = context.getPackageManager().getPackageInfo(
- context.getPackageName(), 0);
-
- int status = NO_DOWNLOAD_REQUIRED;
-
- // the database automatically reads the metadata for version code
- // and download status when the instance is created
- DownloadsDB db = DownloadsDB.getDB(context);
-
- // we need to update the LVL check and get a successful status to
- // proceed
- if (isLVLCheckRequired(db, pi)) {
- status = LVL_CHECK_REQUIRED;
- }
- // we don't have to update LVL. do we still have a download to start?
- if (db.mStatus == 0) {
- DownloadInfo[] infos = db.getDownloads();
- if (null != infos) {
- for (DownloadInfo info : infos) {
- if (!Helpers.doesFileExist(context, info.mFileName, info.mTotalBytes, true)) {
- status = DOWNLOAD_REQUIRED;
- db.updateStatus(-1);
- break;
- }
- }
- }
- } else {
- status = DOWNLOAD_REQUIRED;
- }
- switch (status) {
- case DOWNLOAD_REQUIRED:
- case LVL_CHECK_REQUIRED:
- Intent fileIntent = new Intent();
- fileIntent.setClassName(classPackage, className);
- fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
- context.startService(fileIntent);
- break;
- }
- return status;
- }
-
- @Override
- public void requestAbortDownload() {
- mControl = CONTROL_PAUSED;
- mStatus = STATUS_CANCELED;
- }
-
- @Override
- public void requestPauseDownload() {
- mControl = CONTROL_PAUSED;
- mStatus = STATUS_PAUSED_BY_APP;
- }
-
- @Override
- public void setDownloadFlags(int flags) {
- DownloadsDB.getDB(this).updateFlags(flags);
- }
-
- @Override
- public void requestContinueDownload() {
- if (mControl == CONTROL_PAUSED) {
- mControl = CONTROL_RUN;
- }
- Intent fileIntent = new Intent(this, this.getClass());
- fileIntent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent);
- this.startService(fileIntent);
- }
-
- public abstract String getPublicKey();
-
- public abstract byte[] getSALT();
-
- public abstract String getAlarmReceiverClassName();
-
- private class LVLRunnable implements Runnable {
- LVLRunnable(Context context, PendingIntent intent) {
- mContext = context;
- mPendingIntent = intent;
- }
-
- final Context mContext;
-
- @Override
- public void run() {
- setServiceRunning(true);
- mNotification.onDownloadStateChanged(IDownloaderClient.STATE_FETCHING_URL);
- String deviceId = Secure.getString(mContext.getContentResolver(),
- Secure.ANDROID_ID);
-
- final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
- new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
-
- // reset our policy back to the start of the world to force a
- // re-check
- aep.resetPolicy();
-
- // let's try and get the OBB file from LVL first
- // Construct the LicenseChecker with a Policy.
- final LicenseChecker checker = new LicenseChecker(mContext, aep,
- getPublicKey() // Your public licensing key.
- );
- checker.checkAccess(new LicenseCheckerCallback() {
-
- @Override
- public void allow(int reason) {
- try {
- int count = aep.getExpansionURLCount();
- DownloadsDB db = DownloadsDB.getDB(mContext);
- int status = 0;
- if (count != 0) {
- for (int i = 0; i < count; i++) {
- String currentFileName = aep
- .getExpansionFileName(i);
- if (null != currentFileName) {
- DownloadInfo di = new DownloadInfo(i,
- currentFileName, mContext.getPackageName());
-
- long fileSize = aep.getExpansionFileSize(i);
- if (handleFileUpdated(db, i, currentFileName,
- fileSize)) {
- status |= -1;
- di.resetDownload();
- di.mUri = aep.getExpansionURL(i);
- di.mTotalBytes = fileSize;
- di.mStatus = status;
- db.updateDownload(di);
- } else {
- // we need to read the download
- // information
- // from
- // the database
- DownloadInfo dbdi = db
- .getDownloadInfoByFileName(di.mFileName);
- if (null == dbdi) {
- // the file exists already and is
- // the
- // correct size
- // was delivered by Market or
- // through
- // another mechanism
- Log.d(LOG_TAG, "file " + di.mFileName
- + " found. Not downloading.");
- di.mStatus = STATUS_SUCCESS;
- di.mTotalBytes = fileSize;
- di.mCurrentBytes = fileSize;
- di.mUri = aep.getExpansionURL(i);
- db.updateDownload(di);
- } else if (dbdi.mStatus != STATUS_SUCCESS) {
- // we just update the URL
- dbdi.mUri = aep.getExpansionURL(i);
- db.updateDownload(dbdi);
- status |= -1;
- }
- }
- }
- }
- }
- // first: do we need to do an LVL update?
- // we begin by getting our APK version from the package
- // manager
- PackageInfo pi;
- try {
- pi = mContext.getPackageManager().getPackageInfo(
- mContext.getPackageName(), 0);
- db.updateMetadata(pi.versionCode, status);
- Class<?> serviceClass = DownloaderService.this.getClass();
- switch (startDownloadServiceIfRequired(mContext, mPendingIntent,
- serviceClass)) {
- case NO_DOWNLOAD_REQUIRED:
- mNotification
- .onDownloadStateChanged(IDownloaderClient.STATE_COMPLETED);
- break;
- case LVL_CHECK_REQUIRED:
- // DANGER WILL ROBINSON!
- Log.e(LOG_TAG, "In LVL checking loop!");
- mNotification
- .onDownloadStateChanged(IDownloaderClient.STATE_FAILED_UNLICENSED);
- throw new RuntimeException(
- "Error with LVL checking and database integrity");
- case DOWNLOAD_REQUIRED:
- // do nothing. the download will notify the
- // application
- // when things are done
- break;
- }
- } catch (NameNotFoundException e1) {
- e1.printStackTrace();
- throw new RuntimeException(
- "Error with getting information from package name");
- }
- } finally {
- setServiceRunning(false);
- }
- }
-
- @Override
- public void dontAllow(int reason) {
- try
- {
- switch (reason) {
- case Policy.NOT_LICENSED:
- mNotification
- .onDownloadStateChanged(IDownloaderClient.STATE_FAILED_UNLICENSED);
- break;
- case Policy.RETRY:
- mNotification
- .onDownloadStateChanged(IDownloaderClient.STATE_FAILED_FETCHING_URL);
- break;
- }
- } finally {
- setServiceRunning(false);
- }
-
- }
-
- @Override
- public void applicationError(int errorCode) {
- try {
- mNotification
- .onDownloadStateChanged(IDownloaderClient.STATE_FAILED_FETCHING_URL);
- } finally {
- setServiceRunning(false);
- }
- }
-
- });
-
- }
-
- };
-
- /**
- * Updates the LVL information from the server.
- *
- * @param context
- */
- public void updateLVL(final Context context) {
- Context c = context.getApplicationContext();
- Handler h = new Handler(c.getMainLooper());
- h.post(new LVLRunnable(c, mPendingIntent));
- }
-
- /**
- * The APK has been updated and a filename has been sent down from the
- * Market call. If the file has the same name as the previous file, we do
- * nothing as the file is guaranteed to be the same. If the file does not
- * have the same name, we download it if it hasn't already been delivered by
- * Market.
- *
- * @param index the index of the file from market (0 = main, 1 = patch)
- * @param filename the name of the new file
- * @param fileSize the size of the new file
- * @return
- */
- public boolean handleFileUpdated(DownloadsDB db, int index,
- String filename, long fileSize) {
- DownloadInfo di = db.getDownloadInfoByFileName(filename);
- if (null != di) {
- String oldFile = di.mFileName;
- // cleanup
- if (null != oldFile) {
- if (filename.equals(oldFile)) {
- return false;
- }
-
- // remove partially downloaded file if it is there
- String deleteFile = Helpers.generateSaveFileName(this, oldFile);
- File f = new File(deleteFile);
- if (f.exists())
- f.delete();
- }
- }
- return !Helpers.doesFileExist(this, filename, fileSize, true);
- }
-
- private void scheduleAlarm(long wakeUp) {
- AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- if (alarms == null) {
- Log.e(Constants.TAG, "couldn't get alarm manager");
- return;
- }
-
- if (Constants.LOGV) {
- Log.v(Constants.TAG, "scheduling retry in " + wakeUp + "ms");
- }
-
- String className = getAlarmReceiverClassName();
- Intent intent = new Intent(Constants.ACTION_RETRY);
- intent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent);
- intent.setClassName(this.getPackageName(),
- className);
- mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
- PendingIntent.FLAG_ONE_SHOT);
- alarms.set(
- AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + wakeUp, mAlarmIntent
- );
- }
-
- private void cancelAlarms() {
- if (null != mAlarmIntent) {
- AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- if (alarms == null) {
- Log.e(Constants.TAG, "couldn't get alarm manager");
- return;
- }
- alarms.cancel(mAlarmIntent);
- mAlarmIntent = null;
- }
- }
-
- /**
- * We use this to track network state, such as when WiFi, Cellular, etc. is
- * enabled when downloads are paused or in progress.
- */
- private class InnerBroadcastReceiver extends BroadcastReceiver {
- final Service mService;
-
- InnerBroadcastReceiver(Service service) {
- mService = service;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- pollNetworkState();
- if (mStateChanged
- && !isServiceRunning()) {
- Log.d(Constants.TAG, "InnerBroadcastReceiver Called");
- Intent fileIntent = new Intent(context, mService.getClass());
- fileIntent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent);
- // send a new intent to the service
- context.startService(fileIntent);
- }
- }
- };
-
- /**
- * This is the main thread for the Downloader. This thread is responsible
- * for queuing up downloads and other goodness.
- */
- @Override
- protected void onHandleIntent(Intent intent) {
- setServiceRunning(true);
- try {
- // the database automatically reads the metadata for version code
- // and download status when the instance is created
- DownloadsDB db = DownloadsDB.getDB(this);
- final PendingIntent pendingIntent = (PendingIntent) intent
- .getParcelableExtra(EXTRA_PENDING_INTENT);
-
- if (null != pendingIntent)
- {
- mNotification.setClientIntent(pendingIntent);
- mPendingIntent = pendingIntent;
- } else if (null != mPendingIntent) {
- mNotification.setClientIntent(mPendingIntent);
- } else {
- Log.e(LOG_TAG, "Downloader started in bad state without notification intent.");
- return;
- }
-
- // when the LVL check completes, a successful response will update
- // the service
- if (isLVLCheckRequired(db, mPackageInfo)) {
- updateLVL(this);
- return;
- }
-
- // get each download
- DownloadInfo[] infos = db.getDownloads();
- mBytesSoFar = 0;
- mTotalLength = 0;
- mFileCount = infos.length;
- for (DownloadInfo info : infos) {
- // We do an (simple) integrity check on each file, just to make
- // sure
- if (info.mStatus == STATUS_SUCCESS) {
- // verify that the file matches the state
- if (!Helpers.doesFileExist(this, info.mFileName, info.mTotalBytes, true)) {
- info.mStatus = 0;
- info.mCurrentBytes = 0;
- }
- }
- // get aggregate data
- mTotalLength += info.mTotalBytes;
- mBytesSoFar += info.mCurrentBytes;
- }
-
- // loop through all downloads and fetch them
- pollNetworkState();
- if (null == mConnReceiver) {
-
- /**
- * We use this to track network state, such as when WiFi,
- * Cellular, etc. is enabled when downloads are paused or in
- * progress.
- */
- mConnReceiver = new InnerBroadcastReceiver(this);
- IntentFilter intentFilter = new IntentFilter(
- ConnectivityManager.CONNECTIVITY_ACTION);
- intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- registerReceiver(mConnReceiver, intentFilter);
- }
-
- for (DownloadInfo info : infos) {
- long startingCount = info.mCurrentBytes;
-
- if (info.mStatus != STATUS_SUCCESS) {
- DownloadThread dt = new DownloadThread(info, this, mNotification);
- cancelAlarms();
- scheduleAlarm(Constants.ACTIVE_THREAD_WATCHDOG);
- dt.run();
- cancelAlarms();
- }
- db.updateFromDb(info);
- boolean setWakeWatchdog = false;
- int notifyStatus;
- switch (info.mStatus) {
- case STATUS_FORBIDDEN:
- // the URL is out of date
- updateLVL(this);
- return;
- case STATUS_SUCCESS:
- mBytesSoFar += info.mCurrentBytes - startingCount;
- db.updateMetadata(mPackageInfo.versionCode, 0);
- continue;
- case STATUS_FILE_DELIVERED_INCORRECTLY:
- // we may be on a network that is returning us a web
- // page on redirect
- notifyStatus = IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE;
- info.mCurrentBytes = 0;
- db.updateDownload(info);
- setWakeWatchdog = true;
- break;
- case STATUS_PAUSED_BY_APP:
- notifyStatus = IDownloaderClient.STATE_PAUSED_BY_REQUEST;
- break;
- case STATUS_WAITING_FOR_NETWORK:
- case STATUS_WAITING_TO_RETRY:
- notifyStatus = IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE;
- setWakeWatchdog = true;
- break;
- case STATUS_QUEUED_FOR_WIFI_OR_CELLULAR_PERMISSION:
- case STATUS_QUEUED_FOR_WIFI:
- // look for more detail here
- if (null != mWifiManager) {
- if (!mWifiManager.isWifiEnabled()) {
- notifyStatus = IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION;
- setWakeWatchdog = true;
- break;
- }
- }
- notifyStatus = IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION;
- setWakeWatchdog = true;
- break;
- case STATUS_CANCELED:
- notifyStatus = IDownloaderClient.STATE_FAILED_CANCELED;
- setWakeWatchdog = true;
- break;
-
- case STATUS_INSUFFICIENT_SPACE_ERROR:
- notifyStatus = IDownloaderClient.STATE_FAILED_SDCARD_FULL;
- setWakeWatchdog = true;
- break;
-
- case STATUS_DEVICE_NOT_FOUND_ERROR:
- notifyStatus = IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE;
- setWakeWatchdog = true;
- break;
-
- default:
- notifyStatus = IDownloaderClient.STATE_FAILED;
- break;
- }
- if (setWakeWatchdog) {
- scheduleAlarm(Constants.WATCHDOG_WAKE_TIMER);
- } else {
- cancelAlarms();
- }
- // failure or pause state
- mNotification.onDownloadStateChanged(notifyStatus);
- return;
- }
-
- // all downloads complete
- mNotification.onDownloadStateChanged(IDownloaderClient.STATE_COMPLETED);
- } finally {
- setServiceRunning(false);
- }
- }
-
- @Override
- public void onDestroy() {
- if (null != mConnReceiver) {
- unregisterReceiver(mConnReceiver);
- mConnReceiver = null;
- }
- mServiceStub.disconnect(this);
- super.onDestroy();
- }
-
- public int getNetworkAvailabilityState(DownloadsDB db) {
- if (mIsConnected) {
- if (!mIsCellularConnection)
- return NETWORK_OK;
- int flags = db.mFlags;
- if (mIsRoaming)
- return NETWORK_CANNOT_USE_ROAMING;
- if (0 != (flags & FLAGS_DOWNLOAD_OVER_CELLULAR)) {
- return NETWORK_OK;
- } else {
- return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR;
- }
- }
- return NETWORK_NO_CONNECTION;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- try {
- mPackageInfo = getPackageManager().getPackageInfo(
- getPackageName(), 0);
- ApplicationInfo ai = getApplicationInfo();
- CharSequence applicationLabel = getPackageManager().getApplicationLabel(ai);
- mNotification = new DownloadNotification(this, applicationLabel);
-
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Exception thrown from methods called by generateSaveFile() for any fatal
- * error.
- */
- public static class GenerateSaveFileError extends Exception {
- private static final long serialVersionUID = 3465966015408936540L;
- int mStatus;
- String mMessage;
-
- public GenerateSaveFileError(int status, String message) {
- mStatus = status;
- mMessage = message;
- }
- }
-
- /**
- * Returns the filename (where the file should be saved) from info about a
- * download
- */
- public String generateTempSaveFileName(String fileName) {
- String path = Helpers.getSaveFilePath(this)
- + File.separator + fileName + TEMP_EXT;
- return path;
- }
-
- /**
- * Creates a filename (where the file should be saved) from info about a
- * download.
- */
- public String generateSaveFile(String filename, long filesize)
- throws GenerateSaveFileError {
- String path = generateTempSaveFileName(filename);
- File expPath = new File(path);
- if (!Helpers.isExternalMediaMounted()) {
- Log.d(Constants.TAG, "External media not mounted: " + path);
- throw new GenerateSaveFileError(STATUS_DEVICE_NOT_FOUND_ERROR,
- "external media is not yet mounted");
-
- }
- if (expPath.exists()) {
- Log.d(Constants.TAG, "File already exists: " + path);
- throw new GenerateSaveFileError(STATUS_FILE_ALREADY_EXISTS_ERROR,
- "requested destination file already exists");
- }
- if (Helpers.getAvailableBytes(Helpers.getFilesystemRoot(path)) < filesize) {
- throw new GenerateSaveFileError(STATUS_INSUFFICIENT_SPACE_ERROR,
- "insufficient space on external storage");
- }
- return path;
- }
-
- /**
- * @return a non-localized string appropriate for logging corresponding to
- * one of the NETWORK_* constants.
- */
- public String getLogMessageForNetworkError(int networkError) {
- switch (networkError) {
- case NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE:
- return "download size exceeds recommended limit for mobile network";
-
- case NETWORK_UNUSABLE_DUE_TO_SIZE:
- return "download size exceeds limit for mobile network";
-
- case NETWORK_NO_CONNECTION:
- return "no network connection available";
-
- case NETWORK_CANNOT_USE_ROAMING:
- return "download cannot use the current network connection because it is roaming";
-
- case NETWORK_TYPE_DISALLOWED_BY_REQUESTOR:
- return "download was requested to not use the current network type";
-
- default:
- return "unknown error with network connectivity";
- }
- }
-
- public int getControl() {
- return mControl;
- }
-
- public int getStatus() {
- return mStatus;
- }
-
- /**
- * Calculating a moving average for the speed so we don't get jumpy
- * calculations for time etc.
- */
- static private final float SMOOTHING_FACTOR = 0.005f;
-
- public void notifyUpdateBytes(long totalBytesSoFar) {
- long timeRemaining;
- long currentTime = SystemClock.uptimeMillis();
- if (0 != mMillisecondsAtSample) {
- // we have a sample.
- long timePassed = currentTime - mMillisecondsAtSample;
- long bytesInSample = totalBytesSoFar - mBytesAtSample;
- float currentSpeedSample = (float) bytesInSample / (float) timePassed;
- if (0 != mAverageDownloadSpeed) {
- mAverageDownloadSpeed = SMOOTHING_FACTOR * currentSpeedSample
- + (1 - SMOOTHING_FACTOR) * mAverageDownloadSpeed;
- } else {
- mAverageDownloadSpeed = currentSpeedSample;
- }
- timeRemaining = (long) ((mTotalLength - totalBytesSoFar) / mAverageDownloadSpeed);
- } else {
- timeRemaining = -1;
- }
- mMillisecondsAtSample = currentTime;
- mBytesAtSample = totalBytesSoFar;
- mNotification.onDownloadProgress(
- new DownloadProgressInfo(mTotalLength,
- totalBytesSoFar,
- timeRemaining,
- mAverageDownloadSpeed)
- );
-
- }
-
- @Override
- protected boolean shouldStop() {
- // the database automatically reads the metadata for version code
- // and download status when the instance is created
- DownloadsDB db = DownloadsDB.getDB(this);
- if (db.mStatus == 0) {
- return true;
- }
- return false;
- }
-
- @Override
- public void requestDownloadStatus() {
- mNotification.resendState();
- }
-
- @Override
- public void onClientUpdated(Messenger clientMessenger) {
- this.mClientMessenger = clientMessenger;
- mNotification.setMessenger(mClientMessenger);
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java
deleted file mode 100644
index c658b4cc43..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDoneException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
-import android.provider.BaseColumns;
-import android.util.Log;
-
-public class DownloadsDB {
- private static final String DATABASE_NAME = "DownloadsDB";
- private static final int DATABASE_VERSION = 7;
- public static final String LOG_TAG = DownloadsDB.class.getName();
- final SQLiteOpenHelper mHelper;
- SQLiteStatement mGetDownloadByIndex;
- SQLiteStatement mUpdateCurrentBytes;
- private static DownloadsDB mDownloadsDB;
- long mMetadataRowID = -1;
- int mVersionCode = -1;
- int mStatus = -1;
- int mFlags;
-
- static public synchronized DownloadsDB getDB(Context paramContext) {
- if (null == mDownloadsDB) {
- return new DownloadsDB(paramContext);
- }
- return mDownloadsDB;
- }
-
- private SQLiteStatement getDownloadByIndexStatement() {
- if (null == mGetDownloadByIndex) {
- mGetDownloadByIndex = mHelper.getReadableDatabase().compileStatement(
- "SELECT " + BaseColumns._ID + " FROM "
- + DownloadColumns.TABLE_NAME + " WHERE "
- + DownloadColumns.INDEX + " = ?");
- }
- return mGetDownloadByIndex;
- }
-
- private SQLiteStatement getUpdateCurrentBytesStatement() {
- if (null == mUpdateCurrentBytes) {
- mUpdateCurrentBytes = mHelper.getReadableDatabase().compileStatement(
- "UPDATE " + DownloadColumns.TABLE_NAME + " SET " + DownloadColumns.CURRENTBYTES
- + " = ?" +
- " WHERE " + DownloadColumns.INDEX + " = ?");
- }
- return mUpdateCurrentBytes;
- }
-
- private DownloadsDB(Context paramContext) {
- this.mHelper = new DownloadsContentDBHelper(paramContext);
- final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
- // Query for the version code, the row ID of the metadata (for future
- // updating) the status and the flags
- Cursor cur = sqldb.rawQuery("SELECT " +
- MetadataColumns.APKVERSION + "," +
- BaseColumns._ID + "," +
- MetadataColumns.DOWNLOAD_STATUS + "," +
- MetadataColumns.FLAGS +
- " FROM "
- + MetadataColumns.TABLE_NAME + " LIMIT 1", null);
- if (null != cur && cur.moveToFirst()) {
- mVersionCode = cur.getInt(0);
- mMetadataRowID = cur.getLong(1);
- mStatus = cur.getInt(2);
- mFlags = cur.getInt(3);
- cur.close();
- }
- mDownloadsDB = this;
- }
-
- protected DownloadInfo getDownloadInfoByFileName(String fileName) {
- final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
- Cursor itemcur = null;
- try {
- itemcur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
- DownloadColumns.FILENAME + " = ?",
- new String[] {
- fileName
- }, null, null, null);
- if (null != itemcur && itemcur.moveToFirst()) {
- return getDownloadInfoFromCursor(itemcur);
- }
- } finally {
- if (null != itemcur)
- itemcur.close();
- }
- return null;
- }
-
- public long getIDForDownloadInfo(final DownloadInfo di) {
- return getIDByIndex(di.mIndex);
- }
-
- public long getIDByIndex(int index) {
- SQLiteStatement downloadByIndex = getDownloadByIndexStatement();
- downloadByIndex.clearBindings();
- downloadByIndex.bindLong(1, index);
- try {
- return downloadByIndex.simpleQueryForLong();
- } catch (SQLiteDoneException e) {
- return -1;
- }
- }
-
- public void updateDownloadCurrentBytes(final DownloadInfo di) {
- SQLiteStatement downloadCurrentBytes = getUpdateCurrentBytesStatement();
- downloadCurrentBytes.clearBindings();
- downloadCurrentBytes.bindLong(1, di.mCurrentBytes);
- downloadCurrentBytes.bindLong(2, di.mIndex);
- downloadCurrentBytes.execute();
- }
-
- public void close() {
- this.mHelper.close();
- }
-
- protected static class DownloadsContentDBHelper extends SQLiteOpenHelper {
- DownloadsContentDBHelper(Context paramContext) {
- super(paramContext, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- private String createTableQueryFromArray(String paramString,
- String[][] paramArrayOfString) {
- StringBuilder localStringBuilder = new StringBuilder();
- localStringBuilder.append("CREATE TABLE ");
- localStringBuilder.append(paramString);
- localStringBuilder.append(" (");
- int i = paramArrayOfString.length;
- for (int j = 0;; j++) {
- if (j >= i) {
- localStringBuilder
- .setLength(localStringBuilder.length() - 1);
- localStringBuilder.append(");");
- return localStringBuilder.toString();
- }
- String[] arrayOfString = paramArrayOfString[j];
- localStringBuilder.append(' ');
- localStringBuilder.append(arrayOfString[0]);
- localStringBuilder.append(' ');
- localStringBuilder.append(arrayOfString[1]);
- localStringBuilder.append(',');
- }
- }
-
- /**
- * These two arrays must match and have the same order. For every Schema
- * there must be a corresponding table name.
- */
- static final private String[][][] sSchemas = {
- DownloadColumns.SCHEMA, MetadataColumns.SCHEMA
- };
-
- static final private String[] sTables = {
- DownloadColumns.TABLE_NAME, MetadataColumns.TABLE_NAME
- };
-
- /**
- * Goes through all of the tables in sTables and drops each table if it
- * exists. Altered to no longer make use of reflection.
- */
- private void dropTables(SQLiteDatabase paramSQLiteDatabase) {
- for (String table : sTables) {
- try {
- paramSQLiteDatabase.execSQL("DROP TABLE IF EXISTS " + table);
- } catch (Exception localException) {
- localException.printStackTrace();
- }
- }
- }
-
- /**
- * Goes through all of the tables in sTables and creates a database with
- * the corresponding schema described in sSchemas. Altered to no longer
- * make use of reflection.
- */
- public void onCreate(SQLiteDatabase paramSQLiteDatabase) {
- int numSchemas = sSchemas.length;
- for (int i = 0; i < numSchemas; i++) {
- try {
- String[][] schema = (String[][]) sSchemas[i];
- paramSQLiteDatabase.execSQL(createTableQueryFromArray(
- sTables[i], schema));
- } catch (Exception localException) {
- while (true)
- localException.printStackTrace();
- }
- }
- }
-
- public void onUpgrade(SQLiteDatabase paramSQLiteDatabase,
- int paramInt1, int paramInt2) {
- Log.w(DownloadsContentDBHelper.class.getName(),
- "Upgrading database from version " + paramInt1 + " to "
- + paramInt2 + ", which will destroy all old data");
- dropTables(paramSQLiteDatabase);
- onCreate(paramSQLiteDatabase);
- }
- }
-
- public static class MetadataColumns implements BaseColumns {
- public static final String APKVERSION = "APKVERSION";
- public static final String DOWNLOAD_STATUS = "DOWNLOADSTATUS";
- public static final String FLAGS = "DOWNLOADFLAGS";
-
- public static final String[][] SCHEMA = {
- {
- BaseColumns._ID, "INTEGER PRIMARY KEY"
- },
- {
- APKVERSION, "INTEGER"
- }, {
- DOWNLOAD_STATUS, "INTEGER"
- },
- {
- FLAGS, "INTEGER"
- }
- };
- public static final String TABLE_NAME = "MetadataColumns";
- public static final String _ID = "MetadataColumns._id";
- }
-
- public static class DownloadColumns implements BaseColumns {
- public static final String INDEX = "FILEIDX";
- public static final String URI = "URI";
- public static final String FILENAME = "FN";
- public static final String ETAG = "ETAG";
-
- public static final String TOTALBYTES = "TOTALBYTES";
- public static final String CURRENTBYTES = "CURRENTBYTES";
- public static final String LASTMOD = "LASTMOD";
-
- public static final String STATUS = "STATUS";
- public static final String CONTROL = "CONTROL";
- public static final String NUM_FAILED = "FAILCOUNT";
- public static final String RETRY_AFTER = "RETRYAFTER";
- public static final String REDIRECT_COUNT = "REDIRECTCOUNT";
-
- public static final String[][] SCHEMA = {
- {
- BaseColumns._ID, "INTEGER PRIMARY KEY"
- },
- {
- INDEX, "INTEGER UNIQUE"
- }, {
- URI, "TEXT"
- },
- {
- FILENAME, "TEXT UNIQUE"
- }, {
- ETAG, "TEXT"
- },
- {
- TOTALBYTES, "INTEGER"
- }, {
- CURRENTBYTES, "INTEGER"
- },
- {
- LASTMOD, "INTEGER"
- }, {
- STATUS, "INTEGER"
- },
- {
- CONTROL, "INTEGER"
- }, {
- NUM_FAILED, "INTEGER"
- },
- {
- RETRY_AFTER, "INTEGER"
- }, {
- REDIRECT_COUNT, "INTEGER"
- }
- };
- public static final String TABLE_NAME = "DownloadColumns";
- public static final String _ID = "DownloadColumns._id";
- }
-
- private static final String[] DC_PROJECTION = {
- DownloadColumns.FILENAME,
- DownloadColumns.URI, DownloadColumns.ETAG,
- DownloadColumns.TOTALBYTES, DownloadColumns.CURRENTBYTES,
- DownloadColumns.LASTMOD, DownloadColumns.STATUS,
- DownloadColumns.CONTROL, DownloadColumns.NUM_FAILED,
- DownloadColumns.RETRY_AFTER, DownloadColumns.REDIRECT_COUNT,
- DownloadColumns.INDEX
- };
-
- private static final int FILENAME_IDX = 0;
- private static final int URI_IDX = 1;
- private static final int ETAG_IDX = 2;
- private static final int TOTALBYTES_IDX = 3;
- private static final int CURRENTBYTES_IDX = 4;
- private static final int LASTMOD_IDX = 5;
- private static final int STATUS_IDX = 6;
- private static final int CONTROL_IDX = 7;
- private static final int NUM_FAILED_IDX = 8;
- private static final int RETRY_AFTER_IDX = 9;
- private static final int REDIRECT_COUNT_IDX = 10;
- private static final int INDEX_IDX = 11;
-
- /**
- * This function will add a new file to the database if it does not exist.
- *
- * @param di DownloadInfo that we wish to store
- * @return the row id of the record to be updated/inserted, or -1
- */
- public boolean updateDownload(DownloadInfo di) {
- ContentValues cv = new ContentValues();
- cv.put(DownloadColumns.INDEX, di.mIndex);
- cv.put(DownloadColumns.FILENAME, di.mFileName);
- cv.put(DownloadColumns.URI, di.mUri);
- cv.put(DownloadColumns.ETAG, di.mETag);
- cv.put(DownloadColumns.TOTALBYTES, di.mTotalBytes);
- cv.put(DownloadColumns.CURRENTBYTES, di.mCurrentBytes);
- cv.put(DownloadColumns.LASTMOD, di.mLastMod);
- cv.put(DownloadColumns.STATUS, di.mStatus);
- cv.put(DownloadColumns.CONTROL, di.mControl);
- cv.put(DownloadColumns.NUM_FAILED, di.mNumFailed);
- cv.put(DownloadColumns.RETRY_AFTER, di.mRetryAfter);
- cv.put(DownloadColumns.REDIRECT_COUNT, di.mRedirectCount);
- return updateDownload(di, cv);
- }
-
- public boolean updateDownload(DownloadInfo di, ContentValues cv) {
- long id = di == null ? -1 : getIDForDownloadInfo(di);
- try {
- final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
- if (id != -1) {
- if (1 != sqldb.update(DownloadColumns.TABLE_NAME,
- cv, DownloadColumns._ID + " = " + id, null)) {
- return false;
- }
- } else {
- return -1 != sqldb.insert(DownloadColumns.TABLE_NAME,
- DownloadColumns.URI, cv);
- }
- } catch (android.database.sqlite.SQLiteException ex) {
- ex.printStackTrace();
- }
- return false;
- }
-
- public int getLastCheckedVersionCode() {
- return mVersionCode;
- }
-
- public boolean isDownloadRequired() {
- final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
- Cursor cur = sqldb.rawQuery("SELECT Count(*) FROM "
- + DownloadColumns.TABLE_NAME + " WHERE "
- + DownloadColumns.STATUS + " <> 0", null);
- try {
- if (null != cur && cur.moveToFirst()) {
- return 0 == cur.getInt(0);
- }
- } finally {
- if (null != cur)
- cur.close();
- }
- return true;
- }
-
- public int getFlags() {
- return mFlags;
- }
-
- public boolean updateFlags(int flags) {
- if (mFlags != flags) {
- ContentValues cv = new ContentValues();
- cv.put(MetadataColumns.FLAGS, flags);
- if (updateMetadata(cv)) {
- mFlags = flags;
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- };
-
- public boolean updateStatus(int status) {
- if (mStatus != status) {
- ContentValues cv = new ContentValues();
- cv.put(MetadataColumns.DOWNLOAD_STATUS, status);
- if (updateMetadata(cv)) {
- mStatus = status;
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- };
-
- public boolean updateMetadata(ContentValues cv) {
- final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
- if (-1 == this.mMetadataRowID) {
- long newID = sqldb.insert(MetadataColumns.TABLE_NAME,
- MetadataColumns.APKVERSION, cv);
- if (-1 == newID)
- return false;
- mMetadataRowID = newID;
- } else {
- if (0 == sqldb.update(MetadataColumns.TABLE_NAME, cv,
- BaseColumns._ID + " = " + mMetadataRowID, null))
- return false;
- }
- return true;
- }
-
- public boolean updateMetadata(int apkVersion, int downloadStatus) {
- ContentValues cv = new ContentValues();
- cv.put(MetadataColumns.APKVERSION, apkVersion);
- cv.put(MetadataColumns.DOWNLOAD_STATUS, downloadStatus);
- if (updateMetadata(cv)) {
- mVersionCode = apkVersion;
- mStatus = downloadStatus;
- return true;
- } else {
- return false;
- }
- };
-
- public boolean updateFromDb(DownloadInfo di) {
- final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
- Cursor cur = null;
- try {
- cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
- DownloadColumns.FILENAME + "= ?",
- new String[] {
- di.mFileName
- }, null, null, null);
- if (null != cur && cur.moveToFirst()) {
- setDownloadInfoFromCursor(di, cur);
- return true;
- }
- return false;
- } finally {
- if (null != cur) {
- cur.close();
- }
- }
- }
-
- public void setDownloadInfoFromCursor(DownloadInfo di, Cursor cur) {
- di.mUri = cur.getString(URI_IDX);
- di.mETag = cur.getString(ETAG_IDX);
- di.mTotalBytes = cur.getLong(TOTALBYTES_IDX);
- di.mCurrentBytes = cur.getLong(CURRENTBYTES_IDX);
- di.mLastMod = cur.getLong(LASTMOD_IDX);
- di.mStatus = cur.getInt(STATUS_IDX);
- di.mControl = cur.getInt(CONTROL_IDX);
- di.mNumFailed = cur.getInt(NUM_FAILED_IDX);
- di.mRetryAfter = cur.getInt(RETRY_AFTER_IDX);
- di.mRedirectCount = cur.getInt(REDIRECT_COUNT_IDX);
- }
-
- public DownloadInfo getDownloadInfoFromCursor(Cursor cur) {
- DownloadInfo di = new DownloadInfo(cur.getInt(INDEX_IDX),
- cur.getString(FILENAME_IDX), this.getClass().getPackage()
- .getName());
- setDownloadInfoFromCursor(di, cur);
- return di;
- }
-
- public DownloadInfo[] getDownloads() {
- final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
- Cursor cur = null;
- try {
- cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION, null,
- null, null, null, null);
- if (null != cur && cur.moveToFirst()) {
- DownloadInfo[] retInfos = new DownloadInfo[cur.getCount()];
- int idx = 0;
- do {
- DownloadInfo di = getDownloadInfoFromCursor(cur);
- retInfos[idx++] = di;
- } while (cur.moveToNext());
- return retInfos;
- }
- return null;
- } finally {
- if (null != cur) {
- cur.close();
- }
- }
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java b/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java
deleted file mode 100644
index 3f440e9893..0000000000
--- a/platform/android/java/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.expansion.downloader.impl;
-
-import android.text.format.Time;
-
-import java.util.Calendar;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Helper for parsing an HTTP date.
- */
-public final class HttpDateTime {
-
- /*
- * Regular expression for parsing HTTP-date. Wdy, DD Mon YYYY HH:MM:SS GMT
- * RFC 822, updated by RFC 1123 Weekday, DD-Mon-YY HH:MM:SS GMT RFC 850,
- * obsoleted by RFC 1036 Wdy Mon DD HH:MM:SS YYYY ANSI C's asctime() format
- * with following variations Wdy, DD-Mon-YYYY HH:MM:SS GMT Wdy, (SP)D Mon
- * YYYY HH:MM:SS GMT Wdy,DD Mon YYYY HH:MM:SS GMT Wdy, DD-Mon-YY HH:MM:SS
- * GMT Wdy, DD Mon YYYY HH:MM:SS -HHMM Wdy, DD Mon YYYY HH:MM:SS Wdy Mon
- * (SP)D HH:MM:SS YYYY Wdy Mon DD HH:MM:SS YYYY GMT HH can be H if the first
- * digit is zero. Mon can be the full name of the month.
- */
- private static final String HTTP_DATE_RFC_REGEXP =
- "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
- + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
-
- private static final String HTTP_DATE_ANSIC_REGEXP =
- "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
- + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
-
- /**
- * The compiled version of the HTTP-date regular expressions.
- */
- private static final Pattern HTTP_DATE_RFC_PATTERN =
- Pattern.compile(HTTP_DATE_RFC_REGEXP);
- private static final Pattern HTTP_DATE_ANSIC_PATTERN =
- Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
-
- private static class TimeOfDay {
- TimeOfDay(int h, int m, int s) {
- this.hour = h;
- this.minute = m;
- this.second = s;
- }
-
- int hour;
- int minute;
- int second;
- }
-
- public static long parse(String timeString)
- throws IllegalArgumentException {
-
- int date = 1;
- int month = Calendar.JANUARY;
- int year = 1970;
- TimeOfDay timeOfDay;
-
- Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
- if (rfcMatcher.find()) {
- date = getDate(rfcMatcher.group(1));
- month = getMonth(rfcMatcher.group(2));
- year = getYear(rfcMatcher.group(3));
- timeOfDay = getTime(rfcMatcher.group(4));
- } else {
- Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString);
- if (ansicMatcher.find()) {
- month = getMonth(ansicMatcher.group(1));
- date = getDate(ansicMatcher.group(2));
- timeOfDay = getTime(ansicMatcher.group(3));
- year = getYear(ansicMatcher.group(4));
- } else {
- throw new IllegalArgumentException();
- }
- }
-
- // FIXME: Y2038 BUG!
- if (year >= 2038) {
- year = 2038;
- month = Calendar.JANUARY;
- date = 1;
- }
-
- Time time = new Time(Time.TIMEZONE_UTC);
- time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date,
- month, year);
- return time.toMillis(false /* use isDst */);
- }
-
- private static int getDate(String dateString) {
- if (dateString.length() == 2) {
- return (dateString.charAt(0) - '0') * 10
- + (dateString.charAt(1) - '0');
- } else {
- return (dateString.charAt(0) - '0');
- }
- }
-
- /*
- * jan = 9 + 0 + 13 = 22 feb = 5 + 4 + 1 = 10 mar = 12 + 0 + 17 = 29 apr = 0
- * + 15 + 17 = 32 may = 12 + 0 + 24 = 36 jun = 9 + 20 + 13 = 42 jul = 9 + 20
- * + 11 = 40 aug = 0 + 20 + 6 = 26 sep = 18 + 4 + 15 = 37 oct = 14 + 2 + 19
- * = 35 nov = 13 + 14 + 21 = 48 dec = 3 + 4 + 2 = 9
- */
- private static int getMonth(String monthString) {
- int hash = Character.toLowerCase(monthString.charAt(0)) +
- Character.toLowerCase(monthString.charAt(1)) +
- Character.toLowerCase(monthString.charAt(2)) - 3 * 'a';
- switch (hash) {
- case 22:
- return Calendar.JANUARY;
- case 10:
- return Calendar.FEBRUARY;
- case 29:
- return Calendar.MARCH;
- case 32:
- return Calendar.APRIL;
- case 36:
- return Calendar.MAY;
- case 42:
- return Calendar.JUNE;
- case 40:
- return Calendar.JULY;
- case 26:
- return Calendar.AUGUST;
- case 37:
- return Calendar.SEPTEMBER;
- case 35:
- return Calendar.OCTOBER;
- case 48:
- return Calendar.NOVEMBER;
- case 9:
- return Calendar.DECEMBER;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- private static int getYear(String yearString) {
- if (yearString.length() == 2) {
- int year = (yearString.charAt(0) - '0') * 10
- + (yearString.charAt(1) - '0');
- if (year >= 70) {
- return year + 1900;
- } else {
- return year + 2000;
- }
- } else if (yearString.length() == 3) {
- // According to RFC 2822, three digit years should be added to 1900.
- int year = (yearString.charAt(0) - '0') * 100
- + (yearString.charAt(1) - '0') * 10
- + (yearString.charAt(2) - '0');
- return year + 1900;
- } else if (yearString.length() == 4) {
- return (yearString.charAt(0) - '0') * 1000
- + (yearString.charAt(1) - '0') * 100
- + (yearString.charAt(2) - '0') * 10
- + (yearString.charAt(3) - '0');
- } else {
- return 1970;
- }
- }
-
- private static TimeOfDay getTime(String timeString) {
- // HH might be H
- int i = 0;
- int hour = timeString.charAt(i++) - '0';
- if (timeString.charAt(i) != ':')
- hour = hour * 10 + (timeString.charAt(i++) - '0');
- // Skip ':'
- i++;
-
- int minute = (timeString.charAt(i++) - '0') * 10
- + (timeString.charAt(i++) - '0');
- // Skip ':'
- i++;
-
- int second = (timeString.charAt(i++) - '0') * 10
- + (timeString.charAt(i++) - '0');
-
- return new TimeOfDay(hour, minute, second);
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/AESObfuscator.java b/platform/android/java/src/com/google/android/vending/licensing/AESObfuscator.java
deleted file mode 100644
index d6ccb0c5e4..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/AESObfuscator.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import com.google.android.vending.licensing.util.Base64;
-import com.google.android.vending.licensing.util.Base64DecoderException;
-
-import java.io.UnsupportedEncodingException;
-import java.security.GeneralSecurityException;
-import java.security.spec.KeySpec;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * An Obfuscator that uses AES to encrypt data.
- */
-public class AESObfuscator implements Obfuscator {
- private static final String UTF8 = "UTF-8";
- private static final String KEYGEN_ALGORITHM = "PBEWITHSHAAND256BITAES-CBC-BC";
- private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
- private static final byte[] IV =
- { 16, 74, 71, -80, 32, 101, -47, 72, 117, -14, 0, -29, 70, 65, -12, 74 };
- private static final String header = "com.google.android.vending.licensing.AESObfuscator-1|";
-
- private Cipher mEncryptor;
- private Cipher mDecryptor;
-
- /**
- * @param salt an array of random bytes to use for each (un)obfuscation
- * @param applicationId application identifier, e.g. the package name
- * @param deviceId device identifier. Use as many sources as possible to
- * create this unique identifier.
- */
- public AESObfuscator(byte[] salt, String applicationId, String deviceId) {
- try {
- SecretKeyFactory factory = SecretKeyFactory.getInstance(KEYGEN_ALGORITHM);
- KeySpec keySpec =
- new PBEKeySpec((applicationId + deviceId).toCharArray(), salt, 1024, 256);
- SecretKey tmp = factory.generateSecret(keySpec);
- SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
- mEncryptor = Cipher.getInstance(CIPHER_ALGORITHM);
- mEncryptor.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(IV));
- mDecryptor = Cipher.getInstance(CIPHER_ALGORITHM);
- mDecryptor.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));
- } catch (GeneralSecurityException e) {
- // This can't happen on a compatible Android device.
- throw new RuntimeException("Invalid environment", e);
- }
- }
-
- public String obfuscate(String original, String key) {
- if (original == null) {
- return null;
- }
- try {
- // Header is appended as an integrity check
- return Base64.encode(mEncryptor.doFinal((header + key + original).getBytes(UTF8)));
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Invalid environment", e);
- } catch (GeneralSecurityException e) {
- throw new RuntimeException("Invalid environment", e);
- }
- }
-
- public String unobfuscate(String obfuscated, String key) throws ValidationException {
- if (obfuscated == null) {
- return null;
- }
- try {
- String result = new String(mDecryptor.doFinal(Base64.decode(obfuscated)), UTF8);
- // Check for presence of header. This serves as a final integrity check, for cases
- // where the block size is correct during decryption.
- int headerIndex = result.indexOf(header+key);
- if (headerIndex != 0) {
- throw new ValidationException("Header not found (invalid data or key)" + ":" +
- obfuscated);
- }
- return result.substring(header.length()+key.length(), result.length());
- } catch (Base64DecoderException e) {
- throw new ValidationException(e.getMessage() + ":" + obfuscated);
- } catch (IllegalBlockSizeException e) {
- throw new ValidationException(e.getMessage() + ":" + obfuscated);
- } catch (BadPaddingException e) {
- throw new ValidationException(e.getMessage() + ":" + obfuscated);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("Invalid environment", e);
- }
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/APKExpansionPolicy.java b/platform/android/java/src/com/google/android/vending/licensing/APKExpansionPolicy.java
deleted file mode 100644
index 37fad8926a..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/APKExpansionPolicy.java
+++ /dev/null
@@ -1,414 +0,0 @@
-
-package com.google.android.vending.licensing;
-
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import com.google.android.vending.licensing.util.URIQueryDecoder;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-/**
- * Default policy. All policy decisions are based off of response data received
- * from the licensing service. Specifically, the licensing server sends the
- * following information: response validity period, error retry period,
- * error retry count and a URL for restoring app access in unlicensed cases.
- * <p>
- * These values will vary based on the the way the application is configured in
- * the Google Play publishing console, such as whether the application is
- * marked as free or is within its refund period, as well as how often an
- * application is checking with the licensing service.
- * <p>
- * Developers who need more fine grained control over their application's
- * licensing policy should implement a custom Policy.
- */
-public class APKExpansionPolicy implements Policy {
-
- private static final String TAG = "APKExpansionPolicy";
- private static final String PREFS_FILE = "com.google.android.vending.licensing.APKExpansionPolicy";
- private static final String PREF_LAST_RESPONSE = "lastResponse";
- private static final String PREF_VALIDITY_TIMESTAMP = "validityTimestamp";
- private static final String PREF_RETRY_UNTIL = "retryUntil";
- private static final String PREF_MAX_RETRIES = "maxRetries";
- private static final String PREF_RETRY_COUNT = "retryCount";
- private static final String PREF_LICENSING_URL = "licensingUrl";
- private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
- private static final String DEFAULT_RETRY_UNTIL = "0";
- private static final String DEFAULT_MAX_RETRIES = "0";
- private static final String DEFAULT_RETRY_COUNT = "0";
-
- private static final long MILLIS_PER_MINUTE = 60 * 1000;
-
- private long mValidityTimestamp;
- private long mRetryUntil;
- private long mMaxRetries;
- private long mRetryCount;
- private long mLastResponseTime = 0;
- private int mLastResponse;
- private String mLicensingUrl;
- private PreferenceObfuscator mPreferences;
- private Vector<String> mExpansionURLs = new Vector<String>();
- private Vector<String> mExpansionFileNames = new Vector<String>();
- private Vector<Long> mExpansionFileSizes = new Vector<Long>();
-
- /**
- * The design of the protocol supports n files. Currently the market can
- * only deliver two files. To accommodate this, we have these two constants,
- * but the order is the only relevant thing here.
- */
- public static final int MAIN_FILE_URL_INDEX = 0;
- public static final int PATCH_FILE_URL_INDEX = 1;
-
- /**
- * @param context The context for the current application
- * @param obfuscator An obfuscator to be used with preferences.
- */
- public APKExpansionPolicy(Context context, Obfuscator obfuscator) {
- // Import old values
- SharedPreferences sp = context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
- mPreferences = new PreferenceObfuscator(sp, obfuscator);
- mLastResponse = Integer.parseInt(
- mPreferences.getString(PREF_LAST_RESPONSE, Integer.toString(Policy.RETRY)));
- mValidityTimestamp = Long.parseLong(mPreferences.getString(PREF_VALIDITY_TIMESTAMP,
- DEFAULT_VALIDITY_TIMESTAMP));
- mRetryUntil = Long.parseLong(mPreferences.getString(PREF_RETRY_UNTIL, DEFAULT_RETRY_UNTIL));
- mMaxRetries = Long.parseLong(mPreferences.getString(PREF_MAX_RETRIES, DEFAULT_MAX_RETRIES));
- mRetryCount = Long.parseLong(mPreferences.getString(PREF_RETRY_COUNT, DEFAULT_RETRY_COUNT));
- mLicensingUrl = mPreferences.getString(PREF_LICENSING_URL, null);
- }
-
- /**
- * We call this to guarantee that we fetch a fresh policy from the server.
- * This is to be used if the URL is invalid.
- */
- public void resetPolicy() {
- mPreferences.putString(PREF_LAST_RESPONSE, Integer.toString(Policy.RETRY));
- setRetryUntil(DEFAULT_RETRY_UNTIL);
- setMaxRetries(DEFAULT_MAX_RETRIES);
- setRetryCount(Long.parseLong(DEFAULT_RETRY_COUNT));
- setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
- mPreferences.commit();
- }
-
- /**
- * Process a new response from the license server.
- * <p>
- * This data will be used for computing future policy decisions. The
- * following parameters are processed:
- * <ul>
- * <li>VT: the timestamp that the client should consider the response valid
- * until
- * <li>GT: the timestamp that the client should ignore retry errors until
- * <li>GR: the number of retry errors that the client should ignore
- * <li>LU: a deep link URL that can enable access for unlicensed apps (e.g.
- * buy app on the Play Store)
- * </ul>
- *
- * @param response the result from validating the server response
- * @param rawData the raw server response data
- */
- public void processServerResponse(int response,
- com.google.android.vending.licensing.ResponseData rawData) {
-
- // Update retry counter
- if (response != Policy.RETRY) {
- setRetryCount(0);
- } else {
- setRetryCount(mRetryCount + 1);
- }
-
- // Update server policy data
- Map<String, String> extras = decodeExtras(rawData);
- if (response == Policy.LICENSED) {
- mLastResponse = response;
- // Reset the licensing URL since it is only applicable for NOT_LICENSED responses.
- setLicensingUrl(null);
- setValidityTimestamp(Long.toString(System.currentTimeMillis() + MILLIS_PER_MINUTE));
- Set<String> keys = extras.keySet();
- for (String key : keys) {
- if (key.equals("VT")) {
- setValidityTimestamp(extras.get(key));
- } else if (key.equals("GT")) {
- setRetryUntil(extras.get(key));
- } else if (key.equals("GR")) {
- setMaxRetries(extras.get(key));
- } else if (key.startsWith("FILE_URL")) {
- int index = Integer.parseInt(key.substring("FILE_URL".length())) - 1;
- setExpansionURL(index, extras.get(key));
- } else if (key.startsWith("FILE_NAME")) {
- int index = Integer.parseInt(key.substring("FILE_NAME".length())) - 1;
- setExpansionFileName(index, extras.get(key));
- } else if (key.startsWith("FILE_SIZE")) {
- int index = Integer.parseInt(key.substring("FILE_SIZE".length())) - 1;
- setExpansionFileSize(index, Long.parseLong(extras.get(key)));
- }
- }
- } else if (response == Policy.NOT_LICENSED) {
- // Clear out stale retry params
- setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
- setRetryUntil(DEFAULT_RETRY_UNTIL);
- setMaxRetries(DEFAULT_MAX_RETRIES);
- // Update the licensing URL
- setLicensingUrl(extras.get("LU"));
- }
-
- setLastResponse(response);
- mPreferences.commit();
- }
-
- /**
- * Set the last license response received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param l the response
- */
- private void setLastResponse(int l) {
- mLastResponseTime = System.currentTimeMillis();
- mLastResponse = l;
- mPreferences.putString(PREF_LAST_RESPONSE, Integer.toString(l));
- }
-
- /**
- * Set the current retry count and add to preferences. You must manually
- * call PreferenceObfuscator.commit() to commit these changes to disk.
- *
- * @param c the new retry count
- */
- private void setRetryCount(long c) {
- mRetryCount = c;
- mPreferences.putString(PREF_RETRY_COUNT, Long.toString(c));
- }
-
- public long getRetryCount() {
- return mRetryCount;
- }
-
- /**
- * Set the last validity timestamp (VT) received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param validityTimestamp the VT string received
- */
- private void setValidityTimestamp(String validityTimestamp) {
- Long lValidityTimestamp;
- try {
- lValidityTimestamp = Long.parseLong(validityTimestamp);
- } catch (NumberFormatException e) {
- // No response or not parseable, expire in one minute.
- Log.w(TAG, "License validity timestamp (VT) missing, caching for a minute");
- lValidityTimestamp = System.currentTimeMillis() + MILLIS_PER_MINUTE;
- validityTimestamp = Long.toString(lValidityTimestamp);
- }
-
- mValidityTimestamp = lValidityTimestamp;
- mPreferences.putString(PREF_VALIDITY_TIMESTAMP, validityTimestamp);
- }
-
- public long getValidityTimestamp() {
- return mValidityTimestamp;
- }
-
- /**
- * Set the retry until timestamp (GT) received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param retryUntil the GT string received
- */
- private void setRetryUntil(String retryUntil) {
- Long lRetryUntil;
- try {
- lRetryUntil = Long.parseLong(retryUntil);
- } catch (NumberFormatException e) {
- // No response or not parseable, expire immediately
- Log.w(TAG, "License retry timestamp (GT) missing, grace period disabled");
- retryUntil = "0";
- lRetryUntil = 0l;
- }
-
- mRetryUntil = lRetryUntil;
- mPreferences.putString(PREF_RETRY_UNTIL, retryUntil);
- }
-
- public long getRetryUntil() {
- return mRetryUntil;
- }
-
- /**
- * Set the max retries value (GR) as received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param maxRetries the GR string received
- */
- private void setMaxRetries(String maxRetries) {
- Long lMaxRetries;
- try {
- lMaxRetries = Long.parseLong(maxRetries);
- } catch (NumberFormatException e) {
- // No response or not parseable, expire immediately
- Log.w(TAG, "Licence retry count (GR) missing, grace period disabled");
- maxRetries = "0";
- lMaxRetries = 0l;
- }
-
- mMaxRetries = lMaxRetries;
- mPreferences.putString(PREF_MAX_RETRIES, maxRetries);
- }
-
- public long getMaxRetries() {
- return mMaxRetries;
- }
-
- /**
- * Set the licensing URL that displays a Play Store UI for the user to regain app access.
- *
- * @param url the LU string received
- */
- private void setLicensingUrl(String url) {
- mLicensingUrl = url;
- mPreferences.putString(PREF_LICENSING_URL, url);
- }
-
- public String getLicensingUrl() {
- return mLicensingUrl;
- }
-
- /**
- * Gets the count of expansion URLs. Since expansionURLs are not committed
- * to preferences, this will return zero if there has been no LVL fetch
- * in the current session.
- *
- * @return the number of expansion URLs. (0,1,2)
- */
- public int getExpansionURLCount() {
- return mExpansionURLs.size();
- }
-
- /**
- * Gets the expansion URL. Since these URLs are not committed to
- * preferences, this will always return null if there has not been an LVL
- * fetch in the current session.
- *
- * @param index the index of the URL to fetch. This value will be either
- * MAIN_FILE_URL_INDEX or PATCH_FILE_URL_INDEX
- */
- public String getExpansionURL(int index) {
- if (index < mExpansionURLs.size()) {
- return mExpansionURLs.elementAt(index);
- }
- return null;
- }
-
- /**
- * Sets the expansion URL. Expansion URL's are not committed to preferences,
- * but are instead intended to be stored when the license response is
- * processed by the front-end.
- *
- * @param index the index of the expansion URL. This value will be either
- * MAIN_FILE_URL_INDEX or PATCH_FILE_URL_INDEX
- * @param URL the URL to set
- */
- public void setExpansionURL(int index, String URL) {
- if (index >= mExpansionURLs.size()) {
- mExpansionURLs.setSize(index + 1);
- }
- mExpansionURLs.set(index, URL);
- }
-
- public String getExpansionFileName(int index) {
- if (index < mExpansionFileNames.size()) {
- return mExpansionFileNames.elementAt(index);
- }
- return null;
- }
-
- public void setExpansionFileName(int index, String name) {
- if (index >= mExpansionFileNames.size()) {
- mExpansionFileNames.setSize(index + 1);
- }
- mExpansionFileNames.set(index, name);
- }
-
- public long getExpansionFileSize(int index) {
- if (index < mExpansionFileSizes.size()) {
- return mExpansionFileSizes.elementAt(index);
- }
- return -1;
- }
-
- public void setExpansionFileSize(int index, long size) {
- if (index >= mExpansionFileSizes.size()) {
- mExpansionFileSizes.setSize(index + 1);
- }
- mExpansionFileSizes.set(index, size);
- }
-
- /**
- * {@inheritDoc} This implementation allows access if either:<br>
- * <ol>
- * <li>a LICENSED response was received within the validity period
- * <li>a RETRY response was received in the last minute, and we are under
- * the RETRY count or in the RETRY period.
- * </ol>
- */
- public boolean allowAccess() {
- long ts = System.currentTimeMillis();
- if (mLastResponse == Policy.LICENSED) {
- // Check if the LICENSED response occurred within the validity
- // timeout.
- if (ts <= mValidityTimestamp) {
- // Cached LICENSED response is still valid.
- return true;
- }
- } else if (mLastResponse == Policy.RETRY &&
- ts < mLastResponseTime + MILLIS_PER_MINUTE) {
- // Only allow access if we are within the retry period or we haven't
- // used up our
- // max retries.
- return (ts <= mRetryUntil || mRetryCount <= mMaxRetries);
- }
- return false;
- }
-
- private Map<String, String> decodeExtras(
- com.google.android.vending.licensing.ResponseData rawData) {
- Map<String, String> results = new HashMap<String, String>();
- if (rawData == null) {
- return results;
- }
-
- try {
- URI rawExtras = new URI("?" + rawData.extra);
- URIQueryDecoder.DecodeQuery(rawExtras, results);
- } catch (URISyntaxException e) {
- Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
- }
- return results;
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/DeviceLimiter.java b/platform/android/java/src/com/google/android/vending/licensing/DeviceLimiter.java
deleted file mode 100644
index e5c5e2d7ca..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/DeviceLimiter.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * Allows the developer to limit the number of devices using a single license.
- * <p>
- * The LICENSED response from the server contains a user identifier unique to
- * the &lt;application, user&gt; pair. The developer can send this identifier
- * to their own server along with some device identifier (a random number
- * generated and stored once per application installation,
- * {@link android.telephony.TelephonyManager#getDeviceId getDeviceId},
- * {@link android.provider.Settings.Secure#ANDROID_ID ANDROID_ID}, etc).
- * The more sources used to identify the device, the harder it will be for an
- * attacker to spoof.
- * <p>
- * The server can look at the &lt;application, user, device id&gt; tuple and
- * restrict a user's application license to run on at most 10 different devices
- * in a week (for example). We recommend not being too restrictive because a
- * user might legitimately have multiple devices or be in the process of
- * changing phones. This will catch egregious violations of multiple people
- * sharing one license.
- */
-public interface DeviceLimiter {
-
- /**
- * Checks if this device is allowed to use the given user's license.
- *
- * @param userId the user whose license the server responded with
- * @return LICENSED if the device is allowed, NOT_LICENSED if not, RETRY if an error occurs
- */
- int isDeviceAllowed(String userId);
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java b/platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java
deleted file mode 100644
index 15017b3425..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/LicenseChecker.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.provider.Settings.Secure;
-import android.util.Log;
-
-import com.android.vending.licensing.ILicenseResultListener;
-import com.android.vending.licensing.ILicensingService;
-import com.google.android.vending.licensing.util.Base64;
-import com.google.android.vending.licensing.util.Base64DecoderException;
-
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Queue;
-import java.util.Set;
-
-/**
- * Client library for Google Play license verifications.
- * <p>
- * The LicenseChecker is configured via a {@link Policy} which contains the logic to determine
- * whether a user should have access to the application. For example, the Policy can define a
- * threshold for allowable number of server or client failures before the library reports the user
- * as not having access.
- * <p>
- * Must also provide the Base64-encoded RSA public key associated with your developer account. The
- * public key is obtainable from the publisher site.
- */
-public class LicenseChecker implements ServiceConnection {
- private static final String TAG = "LicenseChecker";
-
- private static final String KEY_FACTORY_ALGORITHM = "RSA";
-
- // Timeout value (in milliseconds) for calls to service.
- private static final int TIMEOUT_MS = 10 * 1000;
-
- private static final SecureRandom RANDOM = new SecureRandom();
- private static final boolean DEBUG_LICENSE_ERROR = false;
-
- private ILicensingService mService;
-
- private PublicKey mPublicKey;
- private final Context mContext;
- private final Policy mPolicy;
- /**
- * A handler for running tasks on a background thread. We don't want license processing to block
- * the UI thread.
- */
- private Handler mHandler;
- private final String mPackageName;
- private final String mVersionCode;
- private final Set<LicenseValidator> mChecksInProgress = new HashSet<LicenseValidator>();
- private final Queue<LicenseValidator> mPendingChecks = new LinkedList<LicenseValidator>();
-
- /**
- * @param context a Context
- * @param policy implementation of Policy
- * @param encodedPublicKey Base64-encoded RSA public key
- * @throws IllegalArgumentException if encodedPublicKey is invalid
- */
- public LicenseChecker(Context context, Policy policy, String encodedPublicKey) {
- mContext = context;
- mPolicy = policy;
- mPublicKey = generatePublicKey(encodedPublicKey);
- mPackageName = mContext.getPackageName();
- mVersionCode = getVersionCode(context, mPackageName);
- HandlerThread handlerThread = new HandlerThread("background thread");
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
- }
-
- /**
- * Generates a PublicKey instance from a string containing the Base64-encoded public key.
- *
- * @param encodedPublicKey Base64-encoded public key
- * @throws IllegalArgumentException if encodedPublicKey is invalid
- */
- private static PublicKey generatePublicKey(String encodedPublicKey) {
- try {
- byte[] decodedKey = Base64.decode(encodedPublicKey);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
-
- return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
- } catch (NoSuchAlgorithmException e) {
- // This won't happen in an Android-compatible environment.
- throw new RuntimeException(e);
- } catch (Base64DecoderException e) {
- Log.e(TAG, "Could not decode from Base64.");
- throw new IllegalArgumentException(e);
- } catch (InvalidKeySpecException e) {
- Log.e(TAG, "Invalid key specification.");
- throw new IllegalArgumentException(e);
- }
- }
-
- /**
- * Checks if the user should have access to the app. Binds the service if necessary.
- * <p>
- * NOTE: This call uses a trivially obfuscated string (base64-encoded). For best security, we
- * recommend obfuscating the string that is passed into bindService using another method of your
- * own devising.
- * <p>
- * source string: "com.android.vending.licensing.ILicensingService"
- * <p>
- *
- * @param callback
- */
- public synchronized void checkAccess(LicenseCheckerCallback callback) {
- // If we have a valid recent LICENSED response, we can skip asking
- // Market.
- if (mPolicy.allowAccess()) {
- Log.i(TAG, "Using cached license response");
- callback.allow(Policy.LICENSED);
- } else {
- LicenseValidator validator = new LicenseValidator(mPolicy, new NullDeviceLimiter(),
- callback, generateNonce(), mPackageName, mVersionCode);
-
- if (mService == null) {
- Log.i(TAG, "Binding to licensing service.");
- try {
- boolean bindResult = mContext
- .bindService(
- new Intent(
- new String(
- // Base64 encoded -
- // com.android.vending.licensing.ILicensingService
- // Consider encoding this in another way in your
- // code to improve security
- Base64.decode(
- "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))
- // As of Android 5.0, implicit
- // Service Intents are no longer
- // allowed because it's not
- // possible for the user to
- // participate in disambiguating
- // them. This does mean we break
- // compatibility with Android
- // Cupcake devices with this
- // release, since setPackage was
- // added in Donut.
- .setPackage(
- new String(
- // Base64
- // encoded -
- // com.android.vending
- Base64.decode(
- "Y29tLmFuZHJvaWQudmVuZGluZw=="))),
- this, // ServiceConnection.
- Context.BIND_AUTO_CREATE);
- if (bindResult) {
- mPendingChecks.offer(validator);
- } else {
- Log.e(TAG, "Could not bind to service.");
- handleServiceConnectionError(validator);
- }
- } catch (SecurityException e) {
- callback.applicationError(LicenseCheckerCallback.ERROR_MISSING_PERMISSION);
- } catch (Base64DecoderException e) {
- e.printStackTrace();
- }
- } else {
- mPendingChecks.offer(validator);
- runChecks();
- }
- }
- }
-
- /**
- * Triggers the last deep link licensing URL returned from the server, which redirects users to a
- * page which enables them to gain access to the app. If no such URL is returned by the server, it
- * will go to the details page of the app in the Play Store.
- */
- public void followLastLicensingUrl(Context context) {
- String licensingUrl = mPolicy.getLicensingUrl();
- if (licensingUrl == null) {
- licensingUrl = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
- }
- Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(licensingUrl));
- context.startActivity(marketIntent);
- }
-
- private void runChecks() {
- LicenseValidator validator;
- while ((validator = mPendingChecks.poll()) != null) {
- try {
- Log.i(TAG, "Calling checkLicense on service for " + validator.getPackageName());
- mService.checkLicense(
- validator.getNonce(), validator.getPackageName(),
- new ResultListener(validator));
- mChecksInProgress.add(validator);
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException in checkLicense call.", e);
- handleServiceConnectionError(validator);
- }
- }
- }
-
- private synchronized void finishCheck(LicenseValidator validator) {
- mChecksInProgress.remove(validator);
- if (mChecksInProgress.isEmpty()) {
- cleanupService();
- }
- }
-
- private class ResultListener extends ILicenseResultListener.Stub {
- private final LicenseValidator mValidator;
- private Runnable mOnTimeout;
-
- public ResultListener(LicenseValidator validator) {
- mValidator = validator;
- mOnTimeout = new Runnable() {
- public void run() {
- Log.i(TAG, "Check timed out.");
- handleServiceConnectionError(mValidator);
- finishCheck(mValidator);
- }
- };
- startTimeout();
- }
-
- private static final int ERROR_CONTACTING_SERVER = 0x101;
- private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
- private static final int ERROR_NON_MATCHING_UID = 0x103;
-
- // Runs in IPC thread pool. Post it to the Handler, so we can guarantee
- // either this or the timeout runs.
- public void verifyLicense(final int responseCode, final String signedData,
- final String signature) {
- mHandler.post(new Runnable() {
- public void run() {
- Log.i(TAG, "Received response.");
- // Make sure it hasn't already timed out.
- if (mChecksInProgress.contains(mValidator)) {
- clearTimeout();
- mValidator.verify(mPublicKey, responseCode, signedData, signature);
- finishCheck(mValidator);
- }
- if (DEBUG_LICENSE_ERROR) {
- boolean logResponse;
- String stringError = null;
- switch (responseCode) {
- case ERROR_CONTACTING_SERVER:
- logResponse = true;
- stringError = "ERROR_CONTACTING_SERVER";
- break;
- case ERROR_INVALID_PACKAGE_NAME:
- logResponse = true;
- stringError = "ERROR_INVALID_PACKAGE_NAME";
- break;
- case ERROR_NON_MATCHING_UID:
- logResponse = true;
- stringError = "ERROR_NON_MATCHING_UID";
- break;
- default:
- logResponse = false;
- }
-
- if (logResponse) {
- String android_id = Secure.getString(mContext.getContentResolver(),
- Secure.ANDROID_ID);
- Date date = new Date();
- Log.d(TAG, "Server Failure: " + stringError);
- Log.d(TAG, "Android ID: " + android_id);
- Log.d(TAG, "Time: " + date.toGMTString());
- }
- }
-
- }
- });
- }
-
- private void startTimeout() {
- Log.i(TAG, "Start monitoring timeout.");
- mHandler.postDelayed(mOnTimeout, TIMEOUT_MS);
- }
-
- private void clearTimeout() {
- Log.i(TAG, "Clearing timeout.");
- mHandler.removeCallbacks(mOnTimeout);
- }
- }
-
- public synchronized void onServiceConnected(ComponentName name, IBinder service) {
- mService = ILicensingService.Stub.asInterface(service);
- runChecks();
- }
-
- public synchronized void onServiceDisconnected(ComponentName name) {
- // Called when the connection with the service has been
- // unexpectedly disconnected. That is, Market crashed.
- // If there are any checks in progress, the timeouts will handle them.
- Log.w(TAG, "Service unexpectedly disconnected.");
- mService = null;
- }
-
- /**
- * Generates policy response for service connection errors, as a result of disconnections or
- * timeouts.
- */
- private synchronized void handleServiceConnectionError(LicenseValidator validator) {
- mPolicy.processServerResponse(Policy.RETRY, null);
-
- if (mPolicy.allowAccess()) {
- validator.getCallback().allow(Policy.RETRY);
- } else {
- validator.getCallback().dontAllow(Policy.RETRY);
- }
- }
-
- /** Unbinds service if necessary and removes reference to it. */
- private void cleanupService() {
- if (mService != null) {
- try {
- mContext.unbindService(this);
- } catch (IllegalArgumentException e) {
- // Somehow we've already been unbound. This is a non-fatal
- // error.
- Log.e(TAG, "Unable to unbind from licensing service (already unbound)");
- }
- mService = null;
- }
- }
-
- /**
- * Inform the library that the context is about to be destroyed, so that any open connections
- * can be cleaned up.
- * <p>
- * Failure to call this method can result in a crash under certain circumstances, such as during
- * screen rotation if an Activity requests the license check or when the user exits the
- * application.
- */
- public synchronized void onDestroy() {
- cleanupService();
- mHandler.getLooper().quit();
- }
-
- /** Generates a nonce (number used once). */
- private int generateNonce() {
- return RANDOM.nextInt();
- }
-
- /**
- * Get version code for the application package name.
- *
- * @param context
- * @param packageName application package name
- * @return the version code or empty string if package not found
- */
- private static String getVersionCode(Context context, String packageName) {
- try {
- return String.valueOf(
- context.getPackageManager().getPackageInfo(packageName, 0).versionCode);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found. could not get version code.");
- return "";
- }
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/LicenseCheckerCallback.java b/platform/android/java/src/com/google/android/vending/licensing/LicenseCheckerCallback.java
deleted file mode 100644
index 8b869ddaaf..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/LicenseCheckerCallback.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * Callback for the license checker library.
- * <p>
- * Upon checking with the Market server and conferring with the {@link Policy},
- * the library calls the appropriate callback method to communicate the result.
- * <p>
- * <b>The callback does not occur in the original checking thread.</b> Your
- * application should post to the appropriate handling thread or lock
- * accordingly.
- * <p>
- * The reason that is passed back with allow/dontAllow is the base status handed
- * to the policy for allowed/disallowing the license. Policy.RETRY will call
- * allow or dontAllow depending on other statistics associated with the policy,
- * while in most cases Policy.NOT_LICENSED will call dontAllow and
- * Policy.LICENSED will Allow.
- */
-public interface LicenseCheckerCallback {
-
- /**
- * Allow use. App should proceed as normal.
- *
- * @param reason Policy.LICENSED or Policy.RETRY typically. (although in
- * theory the policy can return Policy.NOT_LICENSED here as well)
- */
- public void allow(int reason);
-
- /**
- * Don't allow use. App should inform user and take appropriate action.
- *
- * @param reason Policy.NOT_LICENSED or Policy.RETRY. (although in theory
- * the policy can return Policy.LICENSED here as well ---
- * perhaps the call to the LVL took too long, for example)
- */
- public void dontAllow(int reason);
-
- /** Application error codes. */
- public static final int ERROR_INVALID_PACKAGE_NAME = 1;
- public static final int ERROR_NON_MATCHING_UID = 2;
- public static final int ERROR_NOT_MARKET_MANAGED = 3;
- public static final int ERROR_CHECK_IN_PROGRESS = 4;
- public static final int ERROR_INVALID_PUBLIC_KEY = 5;
- public static final int ERROR_MISSING_PERMISSION = 6;
-
- /**
- * Error in application code. Caller did not call or set up license checker
- * correctly. Should be considered fatal.
- */
- public void applicationError(int errorCode);
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/LicenseValidator.java b/platform/android/java/src/com/google/android/vending/licensing/LicenseValidator.java
deleted file mode 100644
index 11a00786d0..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/LicenseValidator.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import com.google.android.vending.licensing.util.Base64;
-import com.google.android.vending.licensing.util.Base64DecoderException;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-
-/**
- * Contains data related to a licensing request and methods to verify
- * and process the response.
- */
-class LicenseValidator {
- private static final String TAG = "LicenseValidator";
-
- // Server response codes.
- private static final int LICENSED = 0x0;
- private static final int NOT_LICENSED = 0x1;
- private static final int LICENSED_OLD_KEY = 0x2;
- private static final int ERROR_NOT_MARKET_MANAGED = 0x3;
- private static final int ERROR_SERVER_FAILURE = 0x4;
- private static final int ERROR_OVER_QUOTA = 0x5;
-
- private static final int ERROR_CONTACTING_SERVER = 0x101;
- private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
- private static final int ERROR_NON_MATCHING_UID = 0x103;
-
- private final Policy mPolicy;
- private final LicenseCheckerCallback mCallback;
- private final int mNonce;
- private final String mPackageName;
- private final String mVersionCode;
- private final DeviceLimiter mDeviceLimiter;
-
- LicenseValidator(Policy policy, DeviceLimiter deviceLimiter, LicenseCheckerCallback callback,
- int nonce, String packageName, String versionCode) {
- mPolicy = policy;
- mDeviceLimiter = deviceLimiter;
- mCallback = callback;
- mNonce = nonce;
- mPackageName = packageName;
- mVersionCode = versionCode;
- }
-
- public LicenseCheckerCallback getCallback() {
- return mCallback;
- }
-
- public int getNonce() {
- return mNonce;
- }
-
- public String getPackageName() {
- return mPackageName;
- }
-
- private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
-
- /**
- * Verifies the response from server and calls appropriate callback method.
- *
- * @param publicKey public key associated with the developer account
- * @param responseCode server response code
- * @param signedData signed data from server
- * @param signature server signature
- */
- public void verify(PublicKey publicKey, int responseCode, String signedData, String signature) {
- String userId = null;
- // Skip signature check for unsuccessful requests
- ResponseData data = null;
- if (responseCode == LICENSED || responseCode == NOT_LICENSED ||
- responseCode == LICENSED_OLD_KEY) {
- // Verify signature.
- try {
- if (TextUtils.isEmpty(signedData)) {
- Log.e(TAG, "Signature verification failed: signedData is empty. " +
- "(Device not signed-in to any Google accounts?)");
- handleInvalidResponse();
- return;
- }
-
- Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
- sig.initVerify(publicKey);
- sig.update(signedData.getBytes());
-
- if (!sig.verify(Base64.decode(signature))) {
- Log.e(TAG, "Signature verification failed.");
- handleInvalidResponse();
- return;
- }
- } catch (NoSuchAlgorithmException e) {
- // This can't happen on an Android compatible device.
- throw new RuntimeException(e);
- } catch (InvalidKeyException e) {
- handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PUBLIC_KEY);
- return;
- } catch (SignatureException e) {
- throw new RuntimeException(e);
- } catch (Base64DecoderException e) {
- Log.e(TAG, "Could not Base64-decode signature.");
- handleInvalidResponse();
- return;
- }
-
- // Parse and validate response.
- try {
- data = ResponseData.parse(signedData);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Could not parse response.");
- handleInvalidResponse();
- return;
- }
-
- if (data.responseCode != responseCode) {
- Log.e(TAG, "Response codes don't match.");
- handleInvalidResponse();
- return;
- }
-
- if (data.nonce != mNonce) {
- Log.e(TAG, "Nonce doesn't match.");
- handleInvalidResponse();
- return;
- }
-
- if (!data.packageName.equals(mPackageName)) {
- Log.e(TAG, "Package name doesn't match.");
- handleInvalidResponse();
- return;
- }
-
- if (!data.versionCode.equals(mVersionCode)) {
- Log.e(TAG, "Version codes don't match.");
- handleInvalidResponse();
- return;
- }
-
- // Application-specific user identifier.
- userId = data.userId;
- if (TextUtils.isEmpty(userId)) {
- Log.e(TAG, "User identifier is empty.");
- handleInvalidResponse();
- return;
- }
- }
-
- switch (responseCode) {
- case LICENSED:
- case LICENSED_OLD_KEY:
- int limiterResponse = mDeviceLimiter.isDeviceAllowed(userId);
- handleResponse(limiterResponse, data);
- break;
- case NOT_LICENSED:
- handleResponse(Policy.NOT_LICENSED, data);
- break;
- case ERROR_CONTACTING_SERVER:
- Log.w(TAG, "Error contacting licensing server.");
- handleResponse(Policy.RETRY, data);
- break;
- case ERROR_SERVER_FAILURE:
- Log.w(TAG, "An error has occurred on the licensing server.");
- handleResponse(Policy.RETRY, data);
- break;
- case ERROR_OVER_QUOTA:
- Log.w(TAG, "Licensing server is refusing to talk to this device, over quota.");
- handleResponse(Policy.RETRY, data);
- break;
- case ERROR_INVALID_PACKAGE_NAME:
- handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PACKAGE_NAME);
- break;
- case ERROR_NON_MATCHING_UID:
- handleApplicationError(LicenseCheckerCallback.ERROR_NON_MATCHING_UID);
- break;
- case ERROR_NOT_MARKET_MANAGED:
- handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED);
- break;
- default:
- Log.e(TAG, "Unknown response code for license check.");
- handleInvalidResponse();
- }
- }
-
- /**
- * Confers with policy and calls appropriate callback method.
- *
- * @param response
- * @param rawData
- */
- private void handleResponse(int response, ResponseData rawData) {
- // Update policy data and increment retry counter (if needed)
- mPolicy.processServerResponse(response, rawData);
-
- // Given everything we know, including cached data, ask the policy if we should grant
- // access.
- if (mPolicy.allowAccess()) {
- mCallback.allow(response);
- } else {
- mCallback.dontAllow(response);
- }
- }
-
- private void handleApplicationError(int code) {
- mCallback.applicationError(code);
- }
-
- private void handleInvalidResponse() {
- mCallback.dontAllow(Policy.NOT_LICENSED);
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/NullDeviceLimiter.java b/platform/android/java/src/com/google/android/vending/licensing/NullDeviceLimiter.java
deleted file mode 100644
index d87af3153f..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/NullDeviceLimiter.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * A DeviceLimiter that doesn't limit the number of devices that can use a
- * given user's license.
- * <p>
- * Unless you have reason to believe that your application is being pirated
- * by multiple users using the same license (signing in to Market as the same
- * user), we recommend you use this implementation.
- */
-public class NullDeviceLimiter implements DeviceLimiter {
-
- public int isDeviceAllowed(String userId) {
- return Policy.LICENSED;
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/Obfuscator.java b/platform/android/java/src/com/google/android/vending/licensing/Obfuscator.java
deleted file mode 100644
index 008c150a8e..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/Obfuscator.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * Interface used as part of a {@link Policy} to allow application authors to obfuscate
- * licensing data that will be stored into a SharedPreferences file.
- * <p>
- * Any transformation scheme must be reversable. Implementing classes may optionally implement an
- * integrity check to further prevent modification to preference data. Implementing classes
- * should use device-specific information as a key in the obfuscation algorithm to prevent
- * obfuscated preferences from being shared among devices.
- */
-public interface Obfuscator {
-
- /**
- * Obfuscate a string that is being stored into shared preferences.
- *
- * @param original The data that is to be obfuscated.
- * @param key The key for the data that is to be obfuscated.
- * @return A transformed version of the original data.
- */
- String obfuscate(String original, String key);
-
- /**
- * Undo the transformation applied to data by the obfuscate() method.
- *
- * @param obfuscated The data that is to be un-obfuscated.
- * @param key The key for the data that is to be un-obfuscated.
- * @return The original data transformed by the obfuscate() method.
- * @throws ValidationException Optionally thrown if a data integrity check fails.
- */
- String unobfuscate(String obfuscated, String key) throws ValidationException;
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/Policy.java b/platform/android/java/src/com/google/android/vending/licensing/Policy.java
deleted file mode 100644
index b672a078b7..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/Policy.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * Policy used by {@link LicenseChecker} to determine whether a user should have
- * access to the application.
- */
-public interface Policy {
-
- /**
- * Change these values to make it more difficult for tools to automatically
- * strip LVL protection from your APK.
- */
-
- /**
- * LICENSED means that the server returned back a valid license response
- */
- public static final int LICENSED = 0x0100;
- /**
- * NOT_LICENSED means that the server returned back a valid license response
- * that indicated that the user definitively is not licensed
- */
- public static final int NOT_LICENSED = 0x0231;
- /**
- * RETRY means that the license response was unable to be determined ---
- * perhaps as a result of faulty networking
- */
- public static final int RETRY = 0x0123;
-
- /**
- * Provide results from contact with the license server. Retry counts are
- * incremented if the current value of response is RETRY. Results will be
- * used for any future policy decisions.
- *
- * @param response the result from validating the server response
- * @param rawData the raw server response data, can be null for RETRY
- */
- void processServerResponse(int response, ResponseData rawData);
-
- /**
- * Check if the user should be allowed access to the application.
- */
- boolean allowAccess();
-
- /**
- * Gets the licensing URL returned by the server that can enable access for unlicensed apps (e.g.
- * buy app on the Play Store).
- */
- String getLicensingUrl();
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java b/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java
deleted file mode 100644
index feb579af04..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/PreferenceObfuscator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import android.content.SharedPreferences;
-import android.util.Log;
-
-/**
- * An wrapper for SharedPreferences that transparently performs data obfuscation.
- */
-public class PreferenceObfuscator {
-
- private static final String TAG = "PreferenceObfuscator";
-
- private final SharedPreferences mPreferences;
- private final Obfuscator mObfuscator;
- private SharedPreferences.Editor mEditor;
-
- /**
- * Constructor.
- *
- * @param sp A SharedPreferences instance provided by the system.
- * @param o The Obfuscator to use when reading or writing data.
- */
- public PreferenceObfuscator(SharedPreferences sp, Obfuscator o) {
- mPreferences = sp;
- mObfuscator = o;
- mEditor = null;
- }
-
- public void putString(String key, String value) {
- if (mEditor == null) {
- mEditor = mPreferences.edit();
- // -- GODOT start --
- mEditor.apply();
- // -- GODOT end --
- }
- String obfuscatedValue = mObfuscator.obfuscate(value, key);
- mEditor.putString(key, obfuscatedValue);
- }
-
- public String getString(String key, String defValue) {
- String result;
- String value = mPreferences.getString(key, null);
- if (value != null) {
- try {
- result = mObfuscator.unobfuscate(value, key);
- } catch (ValidationException e) {
- // Unable to unobfuscate, data corrupt or tampered
- Log.w(TAG, "Validation error while reading preference: " + key);
- result = defValue;
- }
- } else {
- // Preference not found
- result = defValue;
- }
- return result;
- }
-
- public void commit() {
- if (mEditor != null) {
- mEditor.commit();
- mEditor = null;
- }
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/ResponseData.java b/platform/android/java/src/com/google/android/vending/licensing/ResponseData.java
deleted file mode 100644
index 3b5d557e76..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/ResponseData.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import android.text.TextUtils;
-
-import java.util.regex.Pattern;
-
-/**
- * ResponseData from licensing server.
- */
-public class ResponseData {
-
- public int responseCode;
- public int nonce;
- public String packageName;
- public String versionCode;
- public String userId;
- public long timestamp;
- /** Response-specific data. */
- public String extra;
-
- /**
- * Parses response string into ResponseData.
- *
- * @param responseData response data string
- * @throws IllegalArgumentException upon parsing error
- * @return ResponseData object
- */
- public static ResponseData parse(String responseData) {
- // Must parse out main response data and response-specific data.
- int index = responseData.indexOf(':');
- String mainData, extraData;
- if (-1 == index) {
- mainData = responseData;
- extraData = "";
- } else {
- mainData = responseData.substring(0, index);
- extraData = index >= responseData.length() ? "" : responseData.substring(index + 1);
- }
-
- String[] fields = TextUtils.split(mainData, Pattern.quote("|"));
- if (fields.length < 6) {
- throw new IllegalArgumentException("Wrong number of fields.");
- }
-
- ResponseData data = new ResponseData();
- data.extra = extraData;
- data.responseCode = Integer.parseInt(fields[0]);
- data.nonce = Integer.parseInt(fields[1]);
- data.packageName = fields[2];
- data.versionCode = fields[3];
- // Application-specific user identifier.
- data.userId = fields[4];
- data.timestamp = Long.parseLong(fields[5]);
-
- return data;
- }
-
- @Override
- public String toString() {
- return TextUtils.join("|", new Object[] {
- responseCode, nonce, packageName, versionCode,
- userId, timestamp
- });
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/ServerManagedPolicy.java b/platform/android/java/src/com/google/android/vending/licensing/ServerManagedPolicy.java
deleted file mode 100644
index e2f0bfdca8..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/ServerManagedPolicy.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import com.google.android.vending.licensing.util.URIQueryDecoder;
-
-/**
- * Default policy. All policy decisions are based off of response data received
- * from the licensing service. Specifically, the licensing server sends the
- * following information: response validity period, error retry period,
- * error retry count and a URL for restoring app access in unlicensed cases.
- * <p>
- * These values will vary based on the the way the application is configured in
- * the Google Play publishing console, such as whether the application is
- * marked as free or is within its refund period, as well as how often an
- * application is checking with the licensing service.
- * <p>
- * Developers who need more fine grained control over their application's
- * licensing policy should implement a custom Policy.
- */
-public class ServerManagedPolicy implements Policy {
-
- private static final String TAG = "ServerManagedPolicy";
- private static final String PREFS_FILE = "com.google.android.vending.licensing.ServerManagedPolicy";
- private static final String PREF_LAST_RESPONSE = "lastResponse";
- private static final String PREF_VALIDITY_TIMESTAMP = "validityTimestamp";
- private static final String PREF_RETRY_UNTIL = "retryUntil";
- private static final String PREF_MAX_RETRIES = "maxRetries";
- private static final String PREF_RETRY_COUNT = "retryCount";
- private static final String PREF_LICENSING_URL = "licensingUrl";
- private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
- private static final String DEFAULT_RETRY_UNTIL = "0";
- private static final String DEFAULT_MAX_RETRIES = "0";
- private static final String DEFAULT_RETRY_COUNT = "0";
-
- private static final long MILLIS_PER_MINUTE = 60 * 1000;
-
- private long mValidityTimestamp;
- private long mRetryUntil;
- private long mMaxRetries;
- private long mRetryCount;
- private long mLastResponseTime = 0;
- private int mLastResponse;
- private String mLicensingUrl;
- private PreferenceObfuscator mPreferences;
-
- /**
- * @param context The context for the current application
- * @param obfuscator An obfuscator to be used with preferences.
- */
- public ServerManagedPolicy(Context context, Obfuscator obfuscator) {
- // Import old values
- SharedPreferences sp = context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
- mPreferences = new PreferenceObfuscator(sp, obfuscator);
- mLastResponse = Integer.parseInt(
- mPreferences.getString(PREF_LAST_RESPONSE, Integer.toString(Policy.RETRY)));
- mValidityTimestamp = Long.parseLong(mPreferences.getString(PREF_VALIDITY_TIMESTAMP,
- DEFAULT_VALIDITY_TIMESTAMP));
- mRetryUntil = Long.parseLong(mPreferences.getString(PREF_RETRY_UNTIL, DEFAULT_RETRY_UNTIL));
- mMaxRetries = Long.parseLong(mPreferences.getString(PREF_MAX_RETRIES, DEFAULT_MAX_RETRIES));
- mRetryCount = Long.parseLong(mPreferences.getString(PREF_RETRY_COUNT, DEFAULT_RETRY_COUNT));
- mLicensingUrl = mPreferences.getString(PREF_LICENSING_URL, null);
- }
-
- /**
- * Process a new response from the license server.
- * <p>
- * This data will be used for computing future policy decisions. The
- * following parameters are processed:
- * <ul>
- * <li>VT: the timestamp that the client should consider the response valid
- * until
- * <li>GT: the timestamp that the client should ignore retry errors until
- * <li>GR: the number of retry errors that the client should ignore
- * <li>LU: a deep link URL that can enable access for unlicensed apps (e.g.
- * buy app on the Play Store)
- * </ul>
- *
- * @param response the result from validating the server response
- * @param rawData the raw server response data
- */
- public void processServerResponse(int response, ResponseData rawData) {
-
- // Update retry counter
- if (response != Policy.RETRY) {
- setRetryCount(0);
- } else {
- setRetryCount(mRetryCount + 1);
- }
-
- // Update server policy data
- Map<String, String> extras = decodeExtras(rawData);
- if (response == Policy.LICENSED) {
- mLastResponse = response;
- // Reset the licensing URL since it is only applicable for NOT_LICENSED responses.
- setLicensingUrl(null);
- setValidityTimestamp(extras.get("VT"));
- setRetryUntil(extras.get("GT"));
- setMaxRetries(extras.get("GR"));
- } else if (response == Policy.NOT_LICENSED) {
- // Clear out stale retry params
- setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
- setRetryUntil(DEFAULT_RETRY_UNTIL);
- setMaxRetries(DEFAULT_MAX_RETRIES);
- // Update the licensing URL
- setLicensingUrl(extras.get("LU"));
- }
-
- setLastResponse(response);
- mPreferences.commit();
- }
-
- /**
- * Set the last license response received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param l the response
- */
- private void setLastResponse(int l) {
- mLastResponseTime = System.currentTimeMillis();
- mLastResponse = l;
- mPreferences.putString(PREF_LAST_RESPONSE, Integer.toString(l));
- }
-
- /**
- * Set the current retry count and add to preferences. You must manually
- * call PreferenceObfuscator.commit() to commit these changes to disk.
- *
- * @param c the new retry count
- */
- private void setRetryCount(long c) {
- mRetryCount = c;
- mPreferences.putString(PREF_RETRY_COUNT, Long.toString(c));
- }
-
- public long getRetryCount() {
- return mRetryCount;
- }
-
- /**
- * Set the last validity timestamp (VT) received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param validityTimestamp the VT string received
- */
- private void setValidityTimestamp(String validityTimestamp) {
- Long lValidityTimestamp;
- try {
- lValidityTimestamp = Long.parseLong(validityTimestamp);
- } catch (NumberFormatException e) {
- // No response or not parsable, expire in one minute.
- Log.w(TAG, "License validity timestamp (VT) missing, caching for a minute");
- lValidityTimestamp = System.currentTimeMillis() + MILLIS_PER_MINUTE;
- validityTimestamp = Long.toString(lValidityTimestamp);
- }
-
- mValidityTimestamp = lValidityTimestamp;
- mPreferences.putString(PREF_VALIDITY_TIMESTAMP, validityTimestamp);
- }
-
- public long getValidityTimestamp() {
- return mValidityTimestamp;
- }
-
- /**
- * Set the retry until timestamp (GT) received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param retryUntil the GT string received
- */
- private void setRetryUntil(String retryUntil) {
- Long lRetryUntil;
- try {
- lRetryUntil = Long.parseLong(retryUntil);
- } catch (NumberFormatException e) {
- // No response or not parsable, expire immediately
- Log.w(TAG, "License retry timestamp (GT) missing, grace period disabled");
- retryUntil = "0";
- lRetryUntil = 0l;
- }
-
- mRetryUntil = lRetryUntil;
- mPreferences.putString(PREF_RETRY_UNTIL, retryUntil);
- }
-
- public long getRetryUntil() {
- return mRetryUntil;
- }
-
- /**
- * Set the max retries value (GR) as received from the server and add to
- * preferences. You must manually call PreferenceObfuscator.commit() to
- * commit these changes to disk.
- *
- * @param maxRetries the GR string received
- */
- private void setMaxRetries(String maxRetries) {
- Long lMaxRetries;
- try {
- lMaxRetries = Long.parseLong(maxRetries);
- } catch (NumberFormatException e) {
- // No response or not parsable, expire immediately
- Log.w(TAG, "Licence retry count (GR) missing, grace period disabled");
- maxRetries = "0";
- lMaxRetries = 0l;
- }
-
- mMaxRetries = lMaxRetries;
- mPreferences.putString(PREF_MAX_RETRIES, maxRetries);
- }
-
- public long getMaxRetries() {
- return mMaxRetries;
- }
-
- /**
- * Set the license URL value (LU) as received from the server and add to preferences. You must
- * manually call PreferenceObfuscator.commit() to commit these changes to disk.
- *
- * @param url the LU string received
- */
- private void setLicensingUrl(String url) {
- mLicensingUrl = url;
- mPreferences.putString(PREF_LICENSING_URL, url);
- }
-
- public String getLicensingUrl() {
- return mLicensingUrl;
- }
-
- /**
- * {@inheritDoc}
- *
- * This implementation allows access if either:<br>
- * <ol>
- * <li>a LICENSED response was received within the validity period
- * <li>a RETRY response was received in the last minute, and we are under
- * the RETRY count or in the RETRY period.
- * </ol>
- */
- public boolean allowAccess() {
- long ts = System.currentTimeMillis();
- if (mLastResponse == Policy.LICENSED) {
- // Check if the LICENSED response occurred within the validity timeout.
- if (ts <= mValidityTimestamp) {
- // Cached LICENSED response is still valid.
- return true;
- }
- } else if (mLastResponse == Policy.RETRY &&
- ts < mLastResponseTime + MILLIS_PER_MINUTE) {
- // Only allow access if we are within the retry period or we haven't used up our
- // max retries.
- return (ts <= mRetryUntil || mRetryCount <= mMaxRetries);
- }
- return false;
- }
-
- private Map<String, String> decodeExtras(
- com.google.android.vending.licensing.ResponseData rawData) {
- Map<String, String> results = new HashMap<String, String>();
- if (rawData == null) {
- return results;
- }
-
- try {
- URI rawExtras = new URI("?" + rawData.extra);
- URIQueryDecoder.DecodeQuery(rawExtras, results);
- } catch (URISyntaxException e) {
- Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
- }
- return results;
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/StrictPolicy.java b/platform/android/java/src/com/google/android/vending/licensing/StrictPolicy.java
deleted file mode 100644
index c2d55c37f1..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/StrictPolicy.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-import android.util.Log;
-import com.google.android.vending.licensing.util.URIQueryDecoder;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Non-caching policy. All requests will be sent to the licensing service,
- * and no local caching is performed.
- * <p>
- * Using a non-caching policy ensures that there is no local preference data
- * for malicious users to tamper with. As a side effect, applications
- * will not be permitted to run while offline. Developers should carefully
- * weigh the risks of using this Policy over one which implements caching,
- * such as ServerManagedPolicy.
- * <p>
- * Access to the application is only allowed if a LICENSED response is.
- * received. All other responses (including RETRY) will deny access.
- */
-public class StrictPolicy implements Policy {
-
- private static final String TAG = "StrictPolicy";
-
- private int mLastResponse;
- private String mLicensingUrl;
-
- public StrictPolicy() {
- // Set default policy. This will force the application to check the policy on launch.
- mLastResponse = Policy.RETRY;
- mLicensingUrl = null;
- }
-
- /**
- * Process a new response from the license server. Since we aren't
- * performing any caching, this equates to reading the LicenseResponse.
- * Any cache-related ResponseData is ignored, but the licensing URL
- * extra is still extracted in cases where the app is unlicensed.
- *
- * @param response the result from validating the server response
- * @param rawData the raw server response data
- */
- public void processServerResponse(int response, ResponseData rawData) {
- mLastResponse = response;
-
- if (response == Policy.NOT_LICENSED) {
- Map<String, String> extras = decodeExtras(rawData);
- mLicensingUrl = extras.get("LU");
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * This implementation allows access if and only if a LICENSED response
- * was received the last time the server was contacted.
- */
- public boolean allowAccess() {
- return (mLastResponse == Policy.LICENSED);
- }
-
- public String getLicensingUrl() {
- return mLicensingUrl;
- }
-
- private Map<String, String> decodeExtras(
- com.google.android.vending.licensing.ResponseData rawData) {
- Map<String, String> results = new HashMap<String, String>();
- if (rawData == null) {
- return results;
- }
-
- try {
- URI rawExtras = new URI("?" + rawData.extra);
- URIQueryDecoder.DecodeQuery(rawExtras, results);
- } catch (URISyntaxException e) {
- Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
- }
- return results;
- }
-
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/ValidationException.java b/platform/android/java/src/com/google/android/vending/licensing/ValidationException.java
deleted file mode 100644
index ee4df47c68..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/ValidationException.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing;
-
-/**
- * Indicates that an error occurred while validating the integrity of data managed by an
- * {@link Obfuscator}.}
- */
-public class ValidationException extends Exception {
- public ValidationException() {
- super();
- }
-
- public ValidationException(String s) {
- super(s);
- }
-
- private static final long serialVersionUID = 1L;
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java b/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java
deleted file mode 100644
index a8bf65f9ca..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/util/Base64.java
+++ /dev/null
@@ -1,578 +0,0 @@
-// Portions copyright 2002, Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.android.vending.licensing.util;
-
-// This code was converted from code at http://iharder.sourceforge.net/base64/
-// Lots of extraneous features were removed.
-/* The original code said:
- * <p>
- * I am placing this code in the Public Domain. Do with it as you will.
- * This software comes with no guarantees or warranties but with
- * plenty of well-wishing instead!
- * Please visit
- * <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
- * periodically to check for updates or to contribute improvements.
- * </p>
- *
- * @author Robert Harder
- * @author rharder@usa.net
- * @version 1.3
- */
-
-// -- GODOT start --
-import com.godot.game.BuildConfig;
-// -- GODOT end --
-
-/**
- * Base64 converter class. This code is not a full-blown MIME encoder;
- * it simply converts binary data to base64 data and back.
- *
- * <p>Note {@link CharBase64} is a GWT-compatible implementation of this
- * class.
- */
-public class Base64 {
- /** Specify encoding (value is {@code true}). */
- public final static boolean ENCODE = true;
-
- /** Specify decoding (value is {@code false}). */
- public final static boolean DECODE = false;
-
- /** The equals sign (=) as a byte. */
- private final static byte EQUALS_SIGN = (byte) '=';
-
- /** The new line character (\n) as a byte. */
- private final static byte NEW_LINE = (byte) '\n';
-
- /**
- * The 64 valid Base64 values.
- */
- private final static byte[] ALPHABET =
- {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
- (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
- (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
- (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
- (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
- (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
- (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
- (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
- (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
- (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
- (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
- (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
- (byte) '9', (byte) '+', (byte) '/'};
-
- /**
- * The 64 valid web safe Base64 values.
- */
- private final static byte[] WEBSAFE_ALPHABET =
- {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
- (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
- (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
- (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
- (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
- (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
- (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
- (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
- (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
- (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
- (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
- (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
- (byte) '9', (byte) '-', (byte) '_'};
-
- /**
- * Translates a Base64 value to either its 6-bit reconstruction value
- * or a negative number indicating some other meaning.
- **/
- private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
- -5, -5, // Whitespace: Tab and Linefeed
- -9, -9, // Decimal 11 - 12
- -5, // Whitespace: Carriage Return
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
- -9, -9, -9, -9, -9, // Decimal 27 - 31
- -5, // Whitespace: Space
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
- 62, // Plus sign at decimal 43
- -9, -9, -9, // Decimal 44 - 46
- 63, // Slash at decimal 47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
- -9, -9, -9, // Decimal 58 - 60
- -1, // Equals sign at decimal 61
- -9, -9, -9, // Decimal 62 - 64
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
- -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
- -9, -9, -9, -9, -9 // Decimal 123 - 127
- /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
- };
-
- /** The web safe decodabet */
- private final static byte[] WEBSAFE_DECODABET =
- {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
- -5, -5, // Whitespace: Tab and Linefeed
- -9, -9, // Decimal 11 - 12
- -5, // Whitespace: Carriage Return
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
- -9, -9, -9, -9, -9, // Decimal 27 - 31
- -5, // Whitespace: Space
- -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
- 62, // Dash '-' sign at decimal 45
- -9, -9, // Decimal 46-47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
- -9, -9, -9, // Decimal 58 - 60
- -1, // Equals sign at decimal 61
- -9, -9, -9, // Decimal 62 - 64
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
- -9, -9, -9, -9, // Decimal 91-94
- 63, // Underscore '_' at decimal 95
- -9, // Decimal 96
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
- -9, -9, -9, -9, -9 // Decimal 123 - 127
- /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
- -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
- };
-
- // Indicates white space in encoding
- private final static byte WHITE_SPACE_ENC = -5;
- // Indicates equals sign in encoding
- private final static byte EQUALS_SIGN_ENC = -1;
-
- /** Defeats instantiation. */
- private Base64() {
- }
-
- /* ******** E N C O D I N G M E T H O D S ******** */
-
- /**
- * Encodes up to three bytes of the array <var>source</var>
- * and writes the resulting four Base64 bytes to <var>destination</var>.
- * The source and destination arrays can be manipulated
- * anywhere along their length by specifying
- * <var>srcOffset</var> and <var>destOffset</var>.
- * This method does not check to make sure your arrays
- * are large enough to accommodate <var>srcOffset</var> + 3 for
- * the <var>source</var> array or <var>destOffset</var> + 4 for
- * the <var>destination</var> array.
- * The actual number of significant bytes in your array is
- * given by <var>numSigBytes</var>.
- *
- * @param source the array to convert
- * @param srcOffset the index where conversion begins
- * @param numSigBytes the number of significant bytes in your array
- * @param destination the array to hold the conversion
- * @param destOffset the index where output will be put
- * @param alphabet is the encoding alphabet
- * @return the <var>destination</var> array
- * @since 1.3
- */
- private static byte[] encode3to4(byte[] source, int srcOffset,
- int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
- // 1 2 3
- // 01234567890123456789012345678901 Bit position
- // --------000000001111111122222222 Array position from threeBytes
- // --------| || || || | Six bit groups to index alphabet
- // >>18 >>12 >> 6 >> 0 Right shift necessary
- // 0x3f 0x3f 0x3f Additional AND
-
- // Create buffer with zero-padding if there are only one or two
- // significant bytes passed in the array.
- // We have to shift left 24 in order to flush out the 1's that appear
- // when Java treats a value as negative that is cast from a byte to an int.
- int inBuff =
- (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
- | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
- | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
-
- switch (numSigBytes) {
- case 3:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
- return destination;
- case 2:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- destination[destOffset + 3] = EQUALS_SIGN;
- return destination;
- case 1:
- destination[destOffset] = alphabet[(inBuff >>> 18)];
- destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- destination[destOffset + 2] = EQUALS_SIGN;
- destination[destOffset + 3] = EQUALS_SIGN;
- return destination;
- default:
- return destination;
- } // end switch
- } // end encode3to4
-
- /**
- * Encodes a byte array into Base64 notation.
- * Equivalent to calling
- * {@code encodeBytes(source, 0, source.length)}
- *
- * @param source The data to convert
- * @since 1.4
- */
- public static String encode(byte[] source) {
- return encode(source, 0, source.length, ALPHABET, true);
- }
-
- /**
- * Encodes a byte array into web safe Base64 notation.
- *
- * @param source The data to convert
- * @param doPadding is {@code true} to pad result with '=' chars
- * if it does not fall on 3 byte boundaries
- */
- public static String encodeWebSafe(byte[] source, boolean doPadding) {
- return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
- }
-
- /**
- * Encodes a byte array into Base64 notation.
- *
- * @param source The data to convert
- * @param off Offset in array where conversion should begin
- * @param len Length of data to convert
- * @param alphabet is the encoding alphabet
- * @param doPadding is {@code true} to pad result with '=' chars
- * if it does not fall on 3 byte boundaries
- * @since 1.4
- */
- public static String encode(byte[] source, int off, int len, byte[] alphabet,
- boolean doPadding) {
- byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
- int outLen = outBuff.length;
-
- // If doPadding is false, set length to truncate '='
- // padding characters
- while (doPadding == false && outLen > 0) {
- if (outBuff[outLen - 1] != '=') {
- break;
- }
- outLen -= 1;
- }
-
- return new String(outBuff, 0, outLen);
- }
-
- /**
- * Encodes a byte array into Base64 notation.
- *
- * @param source The data to convert
- * @param off Offset in array where conversion should begin
- * @param len Length of data to convert
- * @param alphabet is the encoding alphabet
- * @param maxLineLength maximum length of one line.
- * @return the BASE64-encoded byte array
- */
- public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
- int maxLineLength) {
- int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
- int len43 = lenDiv3 * 4;
- byte[] outBuff = new byte[len43 // Main 4:3
- + (len43 / maxLineLength)]; // New lines
-
- int d = 0;
- int e = 0;
- int len2 = len - 2;
- int lineLength = 0;
- for (; d < len2; d += 3, e += 4) {
-
- // The following block of code is the same as
- // encode3to4( source, d + off, 3, outBuff, e, alphabet );
- // but inlined for faster encoding (~20% improvement)
- int inBuff =
- ((source[d + off] << 24) >>> 8)
- | ((source[d + 1 + off] << 24) >>> 16)
- | ((source[d + 2 + off] << 24) >>> 24);
- outBuff[e] = alphabet[(inBuff >>> 18)];
- outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
- outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
- outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
-
- lineLength += 4;
- if (lineLength == maxLineLength) {
- outBuff[e + 4] = NEW_LINE;
- e++;
- lineLength = 0;
- } // end if: end of line
- } // end for: each piece of array
-
- if (d < len) {
- encode3to4(source, d + off, len - d, outBuff, e, alphabet);
-
- lineLength += 4;
- if (lineLength == maxLineLength) {
- // Add a last newline
- outBuff[e + 4] = NEW_LINE;
- e++;
- }
- e += 4;
- }
-
- // -- GODOT start --
- //assert (e == outBuff.length);
- if (BuildConfig.DEBUG && e != outBuff.length)
- throw new RuntimeException();
- // -- GODOT end --
- return outBuff;
- }
-
-
- /* ******** D E C O D I N G M E T H O D S ******** */
-
-
- /**
- * Decodes four bytes from array <var>source</var>
- * and writes the resulting bytes (up to three of them)
- * to <var>destination</var>.
- * The source and destination arrays can be manipulated
- * anywhere along their length by specifying
- * <var>srcOffset</var> and <var>destOffset</var>.
- * This method does not check to make sure your arrays
- * are large enough to accommodate <var>srcOffset</var> + 4 for
- * the <var>source</var> array or <var>destOffset</var> + 3 for
- * the <var>destination</var> array.
- * This method returns the actual number of bytes that
- * were converted from the Base64 encoding.
- *
- *
- * @param source the array to convert
- * @param srcOffset the index where conversion begins
- * @param destination the array to hold the conversion
- * @param destOffset the index where output will be put
- * @param decodabet the decodabet for decoding Base64 content
- * @return the number of decoded bytes converted
- * @since 1.3
- */
- private static int decode4to3(byte[] source, int srcOffset,
- byte[] destination, int destOffset, byte[] decodabet) {
- // Example: Dk==
- if (source[srcOffset + 2] == EQUALS_SIGN) {
- int outBuff =
- ((decodabet[source[srcOffset]] << 24) >>> 6)
- | ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
-
- destination[destOffset] = (byte) (outBuff >>> 16);
- return 1;
- } else if (source[srcOffset + 3] == EQUALS_SIGN) {
- // Example: DkL=
- int outBuff =
- ((decodabet[source[srcOffset]] << 24) >>> 6)
- | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
- | ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
-
- destination[destOffset] = (byte) (outBuff >>> 16);
- destination[destOffset + 1] = (byte) (outBuff >>> 8);
- return 2;
- } else {
- // Example: DkLE
- int outBuff =
- ((decodabet[source[srcOffset]] << 24) >>> 6)
- | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
- | ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
- | ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
-
- destination[destOffset] = (byte) (outBuff >> 16);
- destination[destOffset + 1] = (byte) (outBuff >> 8);
- destination[destOffset + 2] = (byte) (outBuff);
- return 3;
- }
- } // end decodeToBytes
-
-
- /**
- * Decodes data from Base64 notation.
- *
- * @param s the string to decode (decoded in default encoding)
- * @return the decoded data
- * @since 1.4
- */
- public static byte[] decode(String s) throws Base64DecoderException {
- byte[] bytes = s.getBytes();
- return decode(bytes, 0, bytes.length);
- }
-
- /**
- * Decodes data from web safe Base64 notation.
- * Web safe encoding uses '-' instead of '+', '_' instead of '/'
- *
- * @param s the string to decode (decoded in default encoding)
- * @return the decoded data
- */
- public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
- byte[] bytes = s.getBytes();
- return decodeWebSafe(bytes, 0, bytes.length);
- }
-
- /**
- * Decodes Base64 content in byte array format and returns
- * the decoded byte array.
- *
- * @param source The Base64 encoded data
- * @return decoded data
- * @since 1.3
- * @throws Base64DecoderException
- */
- public static byte[] decode(byte[] source) throws Base64DecoderException {
- return decode(source, 0, source.length);
- }
-
- /**
- * Decodes web safe Base64 content in byte array format and returns
- * the decoded data.
- * Web safe encoding uses '-' instead of '+', '_' instead of '/'
- *
- * @param source the string to decode (decoded in default encoding)
- * @return the decoded data
- */
- public static byte[] decodeWebSafe(byte[] source)
- throws Base64DecoderException {
- return decodeWebSafe(source, 0, source.length);
- }
-
- /**
- * Decodes Base64 content in byte array format and returns
- * the decoded byte array.
- *
- * @param source The Base64 encoded data
- * @param off The offset of where to begin decoding
- * @param len The length of characters to decode
- * @return decoded data
- * @since 1.3
- * @throws Base64DecoderException
- */
- public static byte[] decode(byte[] source, int off, int len)
- throws Base64DecoderException {
- return decode(source, off, len, DECODABET);
- }
-
- /**
- * Decodes web safe Base64 content in byte array format and returns
- * the decoded byte array.
- * Web safe encoding uses '-' instead of '+', '_' instead of '/'
- *
- * @param source The Base64 encoded data
- * @param off The offset of where to begin decoding
- * @param len The length of characters to decode
- * @return decoded data
- */
- public static byte[] decodeWebSafe(byte[] source, int off, int len)
- throws Base64DecoderException {
- return decode(source, off, len, WEBSAFE_DECODABET);
- }
-
- /**
- * Decodes Base64 content using the supplied decodabet and returns
- * the decoded byte array.
- *
- * @param source The Base64 encoded data
- * @param off The offset of where to begin decoding
- * @param len The length of characters to decode
- * @param decodabet the decodabet for decoding Base64 content
- * @return decoded data
- */
- public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
- throws Base64DecoderException {
- int len34 = len * 3 / 4;
- byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
- int outBuffPosn = 0;
-
- byte[] b4 = new byte[4];
- int b4Posn = 0;
- int i = 0;
- byte sbiCrop = 0;
- byte sbiDecode = 0;
- for (i = 0; i < len; i++) {
- sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
- sbiDecode = decodabet[sbiCrop];
-
- if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
- if (sbiDecode >= EQUALS_SIGN_ENC) {
- // An equals sign (for padding) must not occur at position 0 or 1
- // and must be the last byte[s] in the encoded value
- if (sbiCrop == EQUALS_SIGN) {
- int bytesLeft = len - i;
- byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
- if (b4Posn == 0 || b4Posn == 1) {
- throw new Base64DecoderException(
- "invalid padding byte '=' at byte offset " + i);
- } else if ((b4Posn == 3 && bytesLeft > 2)
- || (b4Posn == 4 && bytesLeft > 1)) {
- throw new Base64DecoderException(
- "padding byte '=' falsely signals end of encoded value "
- + "at offset " + i);
- } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
- throw new Base64DecoderException(
- "encoded value has invalid trailing byte");
- }
- break;
- }
-
- b4[b4Posn++] = sbiCrop;
- if (b4Posn == 4) {
- outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
- b4Posn = 0;
- }
- }
- } else {
- throw new Base64DecoderException("Bad Base64 input character at " + i
- + ": " + source[i + off] + "(decimal)");
- }
- }
-
- // Because web safe encoding allows non padding base64 encodes, we
- // need to pad the rest of the b4 buffer with equal signs when
- // b4Posn != 0. There can be at most 2 equal signs at the end of
- // four characters, so the b4 buffer must have two or three
- // characters. This also catches the case where the input is
- // padded with EQUALS_SIGN
- if (b4Posn != 0) {
- if (b4Posn == 1) {
- throw new Base64DecoderException("single trailing character at offset "
- + (len - 1));
- }
- b4[b4Posn++] = EQUALS_SIGN;
- outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
- }
-
- byte[] out = new byte[outBuffPosn];
- System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
- return out;
- }
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/util/Base64DecoderException.java b/platform/android/java/src/com/google/android/vending/licensing/util/Base64DecoderException.java
deleted file mode 100644
index 1aef1b54b8..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/util/Base64DecoderException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2002, Google, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.android.vending.licensing.util;
-
-/**
- * Exception thrown when encountering an invalid Base64 input character.
- *
- * @author nelson
- */
-public class Base64DecoderException extends Exception {
- public Base64DecoderException() {
- super();
- }
-
- public Base64DecoderException(String s) {
- super(s);
- }
-
- private static final long serialVersionUID = 1L;
-}
diff --git a/platform/android/java/src/com/google/android/vending/licensing/util/URIQueryDecoder.java b/platform/android/java/src/com/google/android/vending/licensing/util/URIQueryDecoder.java
deleted file mode 100644
index 5155bf5ac3..0000000000
--- a/platform/android/java/src/com/google/android/vending/licensing/util/URIQueryDecoder.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.vending.licensing.util;
-
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URLDecoder;
-import java.util.Map;
-import java.util.Scanner;
-
-public class URIQueryDecoder {
- private static final String TAG = "URIQueryDecoder";
-
- /**
- * Decodes the query portion of the passed-in URI.
- *
- * @param encodedURI the URI containing the query to decode
- * @param results a map containing all query parameters. Query parameters that do not have a
- * value will map to a null string
- */
- static public void DecodeQuery(URI encodedURI, Map<String, String> results) {
- Scanner scanner = new Scanner(encodedURI.getRawQuery());
- scanner.useDelimiter("&");
- try {
- while (scanner.hasNext()) {
- String param = scanner.next();
- String[] valuePair = param.split("=");
- String name, value;
- if (valuePair.length == 1) {
- value = null;
- } else if (valuePair.length == 2) {
- value = URLDecoder.decode(valuePair[1], "UTF-8");
- } else {
- throw new IllegalArgumentException("query parameter invalid");
- }
- name = URLDecoder.decode(valuePair[0], "UTF-8");
- results.put(name, value);
- }
- } catch (UnsupportedEncodingException e) {
- // This should never happen.
- Log.e(TAG, "UTF-8 Not Recognized as a charset. Device configuration Error.");
- }
- }
-}