├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── lvl_sample
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ └── icon.png
│ │ │ ├── layout
│ │ │ │ └── main.xml
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── google
│ │ │ └── play
│ │ │ └── licensing
│ │ │ └── MainActivity.java
│ └── androidTest
│ │ ├── res
│ │ ├── drawable
│ │ │ └── icon.png
│ │ ├── values
│ │ │ └── strings.xml
│ │ └── layout
│ │ │ └── main.xml
│ │ └── java
│ │ └── com
│ │ └── google
│ │ └── android
│ │ └── vending
│ │ └── licensing
│ │ ├── StrictPolicyTest.java
│ │ ├── ObfuscatedPreferencesTest.java
│ │ ├── ServerManagedPolicyTest.java
│ │ ├── AESObfuscatorTest.java
│ │ └── APKExpansionPolicyTest.java
└── build.gradle
├── README.txt
├── lvl_library
├── build.gradle
└── src
│ └── main
│ ├── aidl
│ └── com
│ │ └── android
│ │ └── vending
│ │ └── licensing
│ │ ├── ILicenseResultListener.aidl
│ │ └── ILicensingService.aidl
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── google
│ └── android
│ └── vending
│ └── licensing
│ ├── util
│ ├── Base64DecoderException.java
│ ├── URIQueryDecoder.java
│ └── Base64.java
│ ├── ValidationException.java
│ ├── NullDeviceLimiter.java
│ ├── Obfuscator.java
│ ├── DeviceLimiter.java
│ ├── Policy.java
│ ├── PreferenceObfuscator.java
│ ├── ResponseData.java
│ ├── LicenseCheckerCallback.java
│ ├── StrictPolicy.java
│ ├── AESObfuscator.java
│ ├── LicenseValidator.java
│ ├── ServerManagedPolicy.java
│ ├── APKExpansionPolicy.java
│ └── LicenseChecker.java
├── .gitignore
├── CONTRIBUTING.md
├── gradlew.bat
├── gradlew
├── LICENSE.txt
├── source.properties
└── package.xml
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lvl_library'
2 | include ':lvl_sample'
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/play-licensing/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/lvl_sample/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/play-licensing/HEAD/lvl_sample/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/lvl_sample/src/androidTest/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/play-licensing/HEAD/lvl_sample/src/androidTest/res/drawable/icon.png
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | Client library for the Google Play licensing server.
2 |
3 | See the licensing documentation at https://developer.android.com/guide/publishing/licensing.html
4 |
--------------------------------------------------------------------------------
/lvl_sample/src/androidTest/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
23 | * Unless you have reason to believe that your application is being pirated
24 | * by multiple users using the same license (signing in to Market as the same
25 | * user), we recommend you use this implementation.
26 | */
27 | public class NullDeviceLimiter implements DeviceLimiter {
28 |
29 | public int isDeviceAllowed(String userId) {
30 | return Policy.LICENSED;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lvl_sample/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
23 | * Any transformation scheme must be reversable. Implementing classes may optionally implement an 24 | * integrity check to further prevent modification to preference data. Implementing classes 25 | * should use device-specific information as a key in the obfuscation algorithm to prevent 26 | * obfuscated preferences from being shared among devices. 27 | */ 28 | public interface Obfuscator { 29 | 30 | /** 31 | * Obfuscate a string that is being stored into shared preferences. 32 | * 33 | * @param original The data that is to be obfuscated. 34 | * @param key The key for the data that is to be obfuscated. 35 | * @return A transformed version of the original data. 36 | */ 37 | String obfuscate(String original, String key); 38 | 39 | /** 40 | * Undo the transformation applied to data by the obfuscate() method. 41 | * 42 | * @param obfuscated The data that is to be un-obfuscated. 43 | * @param key The key for the data that is to be un-obfuscated. 44 | * @return The original data transformed by the obfuscate() method. 45 | * @throws ValidationException Optionally thrown if a data integrity check fails. 46 | */ 47 | String unobfuscate(String obfuscated, String key) throws ValidationException; 48 | } 49 | -------------------------------------------------------------------------------- /lvl_library/src/main/java/com/google/android/vending/licensing/DeviceLimiter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.android.vending.licensing; 18 | 19 | /** 20 | * Allows the developer to limit the number of devices using a single license. 21 | *
22 | * The LICENSED response from the server contains a user identifier unique to 23 | * the <application, user> pair. The developer can send this identifier 24 | * to their own server along with some device identifier (a random number 25 | * generated and stored once per application installation, 26 | * {@link android.telephony.TelephonyManager#getDeviceId getDeviceId}, 27 | * {@link android.provider.Settings.Secure#ANDROID_ID ANDROID_ID}, etc). 28 | * The more sources used to identify the device, the harder it will be for an 29 | * attacker to spoof. 30 | *
31 | * The server can look at the <application, user, device id> tuple and
32 | * restrict a user's application license to run on at most 10 different devices
33 | * in a week (for example). We recommend not being too restrictive because a
34 | * user might legitimately have multiple devices or be in the process of
35 | * changing phones. This will catch egregious violations of multiple people
36 | * sharing one license.
37 | */
38 | public interface DeviceLimiter {
39 |
40 | /**
41 | * Checks if this device is allowed to use the given user's license.
42 | *
43 | * @param userId the user whose license the server responded with
44 | * @return LICENSED if the device is allowed, NOT_LICENSED if not, RETRY if an error occurs
45 | */
46 | int isDeviceAllowed(String userId);
47 | }
48 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/Policy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | /**
20 | * Policy used by {@link LicenseChecker} to determine whether a user should have
21 | * access to the application.
22 | */
23 | public interface Policy {
24 |
25 | /**
26 | * Change these values to make it more difficult for tools to automatically
27 | * strip LVL protection from your APK.
28 | */
29 |
30 | /**
31 | * LICENSED means that the server returned back a valid license response
32 | */
33 | public static final int LICENSED = 0x0100;
34 | /**
35 | * NOT_LICENSED means that the server returned back a valid license response
36 | * that indicated that the user definitively is not licensed
37 | */
38 | public static final int NOT_LICENSED = 0x0231;
39 | /**
40 | * RETRY means that the license response was unable to be determined ---
41 | * perhaps as a result of faulty networking
42 | */
43 | public static final int RETRY = 0x0123;
44 |
45 | /**
46 | * Provide results from contact with the license server. Retry counts are
47 | * incremented if the current value of response is RETRY. Results will be
48 | * used for any future policy decisions.
49 | *
50 | * @param response the result from validating the server response
51 | * @param rawData the raw server response data, can be null for RETRY
52 | */
53 | void processServerResponse(int response, ResponseData rawData);
54 |
55 | /**
56 | * Check if the user should be allowed access to the application.
57 | */
58 | boolean allowAccess();
59 |
60 | /**
61 | * Gets the licensing URL returned by the server that can enable access for unlicensed apps (e.g.
62 | * buy app on the Play Store).
63 | */
64 | String getLicensingUrl();
65 | }
66 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/util/URIQueryDecoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing.util;
18 |
19 | import android.util.Log;
20 |
21 | import java.io.UnsupportedEncodingException;
22 | import java.net.URI;
23 | import java.net.URLDecoder;
24 | import java.util.Map;
25 | import java.util.Scanner;
26 |
27 | public class URIQueryDecoder {
28 | private static final String TAG = "URIQueryDecoder";
29 |
30 | /**
31 | * Decodes the query portion of the passed-in URI.
32 | *
33 | * @param encodedURI the URI containing the query to decode
34 | * @param results a map containing all query parameters. Query parameters that do not have a
35 | * value will map to a null string
36 | */
37 | static public void DecodeQuery(URI encodedURI, Map
22 | * Upon checking with the Market server and conferring with the {@link Policy},
23 | * the library calls the appropriate callback method to communicate the result.
24 | *
25 | * The callback does not occur in the original checking thread. Your
26 | * application should post to the appropriate handling thread or lock
27 | * accordingly.
28 | *
29 | * The reason that is passed back with allow/dontAllow is the base status handed
30 | * to the policy for allowed/disallowing the license. Policy.RETRY will call
31 | * allow or dontAllow depending on other statistics associated with the policy,
32 | * while in most cases Policy.NOT_LICENSED will call dontAllow and
33 | * Policy.LICENSED will Allow.
34 | */
35 | public interface LicenseCheckerCallback {
36 |
37 | /**
38 | * Allow use. App should proceed as normal.
39 | *
40 | * @param reason Policy.LICENSED or Policy.RETRY typically. (although in
41 | * theory the policy can return Policy.NOT_LICENSED here as well)
42 | */
43 | public void allow(int reason);
44 |
45 | /**
46 | * Don't allow use. App should inform user and take appropriate action.
47 | *
48 | * @param reason Policy.NOT_LICENSED or Policy.RETRY. (although in theory
49 | * the policy can return Policy.LICENSED here as well ---
50 | * perhaps the call to the LVL took too long, for example)
51 | */
52 | public void dontAllow(int reason);
53 |
54 | /** Application error codes. */
55 | public static final int ERROR_INVALID_PACKAGE_NAME = 1;
56 | public static final int ERROR_NON_MATCHING_UID = 2;
57 | public static final int ERROR_NOT_MARKET_MANAGED = 3;
58 | public static final int ERROR_CHECK_IN_PROGRESS = 4;
59 | public static final int ERROR_INVALID_PUBLIC_KEY = 5;
60 | public static final int ERROR_MISSING_PERMISSION = 6;
61 |
62 | /**
63 | * Error in application code. Caller did not call or set up license checker
64 | * correctly. Should be considered fatal.
65 | */
66 | public void applicationError(int errorCode);
67 | }
68 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/lvl_sample/src/androidTest/java/com/google/android/vending/licensing/StrictPolicyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | import android.support.test.runner.AndroidJUnit4;
20 |
21 | import com.google.android.vending.licensing.Policy;
22 | import com.google.android.vending.licensing.StrictPolicy;
23 |
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 |
27 | import static org.junit.Assert.assertEquals;
28 | import static org.junit.Assert.assertFalse;
29 | import static org.junit.Assert.assertTrue;
30 |
31 | /**
32 | * Test suite for StrictPolicy.
33 | */
34 | @RunWith(AndroidJUnit4.class)
35 | public class StrictPolicyTest {
36 |
37 | /**
38 | * Verify that initial response is to deny access.
39 | */
40 | @Test
41 | public void initialResponse() {
42 | StrictPolicy p = new StrictPolicy();
43 | boolean result = p.allowAccess();
44 | assertFalse(result);
45 | }
46 |
47 | /**
48 | * Verify that after receiving a LICENSED response, the policy grants
49 | * access.
50 | */
51 | @Test
52 | public void licensedResponse() {
53 | StrictPolicy p = new StrictPolicy();
54 | p.processServerResponse(Policy.LICENSED, null);
55 | boolean result = p.allowAccess();
56 | assertTrue(result);
57 | }
58 |
59 | /**
60 | * Verify that after receiving a NOT_LICENSED response, the policy denies
61 | * access and extracts the licensing URL from the response.
62 | */
63 | @Test
64 | public void notLicensedResponse() {
65 | StrictPolicy p = new StrictPolicy();
66 |
67 | String sampleResponse = "0|1579380448|com.example.android.market.licensing|1|" +
68 | "ADf8I4ajjgc1P5ZI1S1DN/YIPIUNPECLrg==|1279578835423:" +
69 | "LU=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.example.android.market.licensing";
70 | p.processServerResponse(Policy.NOT_LICENSED, ResponseData.parse(sampleResponse));
71 | boolean result = p.allowAccess();
72 | assertFalse(result);
73 | assertEquals(
74 | "https://play.google.com/store/apps/details?id=com.example.android.market.licensing",
75 | p.getLicensingUrl());
76 | }
77 |
78 | /**
79 | * Verify that after receiving a RETRY response, the policy denies
80 | * access.
81 | */
82 | @Test
83 | public void retryResponse() {
84 | StrictPolicy p = new StrictPolicy();
85 | p.processServerResponse(Policy.RETRY, null);
86 | boolean result = p.allowAccess();
87 | assertFalse(result);
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/lvl_sample/src/androidTest/java/com/google/android/vending/licensing/ObfuscatedPreferencesTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | import com.google.android.vending.licensing.AESObfuscator;
20 | import com.google.android.vending.licensing.Obfuscator;
21 | import com.google.android.vending.licensing.PreferenceObfuscator;
22 |
23 | import android.content.Context;
24 | import android.content.SharedPreferences;
25 | import android.provider.Settings;
26 | import android.support.test.InstrumentationRegistry;
27 | import android.support.test.filters.SmallTest;
28 | import android.support.test.runner.AndroidJUnit4;
29 |
30 | import org.junit.After;
31 | import org.junit.Before;
32 | import org.junit.Test;
33 | import org.junit.runner.RunWith;
34 |
35 | import static org.junit.Assert.assertEquals;
36 |
37 |
38 | /**
39 | * Test suite for PreferenceObfuscator.
40 | */
41 | @RunWith(AndroidJUnit4.class)
42 | @SmallTest
43 | public class ObfuscatedPreferencesTest {
44 |
45 | private static final String filename =
46 | "com.android.vending.licnese.test.ObfuscatedPreferencePopulatedTest";
47 | private SharedPreferences sp;
48 | private PreferenceObfuscator op;
49 |
50 | @Before
51 | public void initFixture() {
52 | final byte[] SALT = new byte[] {
53 | 104, -12, 112, 82, -85, -10, -11, 61, 15, 54, 44, -66, -117, -89, -64, 110, -53, 123, 33
54 | };
55 |
56 | // Prepare PreferenceObfuscator instance
57 | Context ctx = InstrumentationRegistry.getTargetContext();
58 | sp = ctx.getSharedPreferences(filename, Context.MODE_PRIVATE);
59 | String deviceId = Settings.Secure.getString(
60 | ctx.getContentResolver(),
61 | Settings.Secure.ANDROID_ID);
62 | Obfuscator o = new AESObfuscator(SALT, ctx.getPackageName(), deviceId);
63 | op = new PreferenceObfuscator(sp, o);
64 |
65 | // Populate with test data
66 | op.putString("testString", "Hello world");
67 | op.commit();
68 | }
69 |
70 | @After
71 | public void cleanup() {
72 | // Manually clear out any saved preferences
73 | SharedPreferences.Editor spe = sp.edit();
74 | spe.clear();
75 | spe.commit();
76 | }
77 |
78 | @Test
79 | public void getString() {
80 | assertEquals("Hello world", op.getString("testString", "fail"));
81 | }
82 |
83 | @Test
84 | public void getDefaultString() {
85 | assertEquals("Android rocks", op.getString("noExist", "Android rocks"));
86 | }
87 |
88 | @Test
89 | public void getDefaultNullString() {
90 | assertEquals(null, op.getString("noExist", null));
91 | }
92 |
93 | @Test
94 | public void corruptDataRetunsDefaultString() {
95 | // Insert non-obfuscated string
96 | SharedPreferences.Editor spe = sp.edit();
97 | spe.putString("corruptData", "foo");
98 | spe.commit();
99 |
100 | // Read back contents
101 | assertEquals("Android rocks", op.getString("corruptdata", "Android rocks"));
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/StrictPolicy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | import android.util.Log;
20 | import com.google.android.vending.licensing.util.URIQueryDecoder;
21 | import java.net.URI;
22 | import java.net.URISyntaxException;
23 | import java.util.HashMap;
24 | import java.util.Map;
25 |
26 | /**
27 | * Non-caching policy. All requests will be sent to the licensing service,
28 | * and no local caching is performed.
29 | *
30 | * Using a non-caching policy ensures that there is no local preference data
31 | * for malicious users to tamper with. As a side effect, applications
32 | * will not be permitted to run while offline. Developers should carefully
33 | * weigh the risks of using this Policy over one which implements caching,
34 | * such as ServerManagedPolicy.
35 | *
36 | * Access to the application is only allowed if a LICENSED response is.
37 | * received. All other responses (including RETRY) will deny access.
38 | */
39 | public class StrictPolicy implements Policy {
40 |
41 | private static final String TAG = "StrictPolicy";
42 |
43 | private int mLastResponse;
44 | private String mLicensingUrl;
45 |
46 | public StrictPolicy() {
47 | // Set default policy. This will force the application to check the policy on launch.
48 | mLastResponse = Policy.RETRY;
49 | mLicensingUrl = null;
50 | }
51 |
52 | /**
53 | * Process a new response from the license server. Since we aren't
54 | * performing any caching, this equates to reading the LicenseResponse.
55 | * Any cache-related ResponseData is ignored, but the licensing URL
56 | * extra is still extracted in cases where the app is unlicensed.
57 | *
58 | * @param response the result from validating the server response
59 | * @param rawData the raw server response data
60 | */
61 | public void processServerResponse(int response, ResponseData rawData) {
62 | mLastResponse = response;
63 |
64 | if (response == Policy.NOT_LICENSED) {
65 | Map
43 | * The first thing you need to do is get your hands on your public key.
44 | * Update the BASE64_PUBLIC_KEY constant below with the encoded public key
45 | * for your application, which you can find under Services and APIs/Licensing
46 | * & In-App Billing on the Google Play publisher site.
47 | *
48 | * After you get this sample running, peruse the
49 | *
50 | * licensing documentation.
51 | */
52 | public class MainActivity extends Activity {
53 | private static final String BASE64_PUBLIC_KEY = "REPLACE THIS WITH YOUR PUBLIC KEY";
54 |
55 | // Generate your own 20 random bytes, and put them here.
56 | private static final byte[] SALT = new byte[] {
57 | -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64,
58 | 89
59 | };
60 |
61 | private TextView mStatusText;
62 | private Button mCheckLicenseButton;
63 |
64 | private LicenseCheckerCallback mLicenseCheckerCallback;
65 | private LicenseChecker mChecker;
66 | // A handler on the UI thread.
67 | private Handler mHandler;
68 |
69 | @Override
70 | public void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
73 | setContentView(R.layout.main);
74 |
75 | mStatusText = (TextView) findViewById(R.id.status_text);
76 | mCheckLicenseButton = (Button) findViewById(R.id.check_license_button);
77 | mCheckLicenseButton.setOnClickListener(new View.OnClickListener() {
78 | public void onClick(View view) {
79 | doCheck();
80 | }
81 | });
82 |
83 | mHandler = new Handler();
84 |
85 | // Try to use more data here. ANDROID_ID is a single point of attack.
86 | String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
87 |
88 | // Library calls this when it's done.
89 | mLicenseCheckerCallback = new MyLicenseCheckerCallback();
90 | // Construct the LicenseChecker with a policy.
91 | mChecker = new LicenseChecker(
92 | this, new ServerManagedPolicy(this,
93 | new AESObfuscator(SALT, getPackageName(), deviceId)),
94 | BASE64_PUBLIC_KEY);
95 | doCheck();
96 | }
97 |
98 | protected Dialog onCreateDialog(int id) {
99 | final boolean bRetry = id == 1;
100 | return new AlertDialog.Builder(this)
101 | .setTitle(R.string.unlicensed_dialog_title)
102 | .setMessage(bRetry ? R.string.unlicensed_dialog_retry_body : R.string.unlicensed_dialog_body)
103 | .setPositiveButton(bRetry ? R.string.retry_button : R.string.restore_access_button,
104 | new DialogInterface.OnClickListener() {
105 | boolean mRetry = bRetry;
106 | public void onClick(DialogInterface dialog, int which) {
107 | if ( mRetry ) {
108 | doCheck();
109 | } else {
110 | mChecker.followLastLicensingUrl(MainActivity.this);
111 | }
112 | }
113 | })
114 | .setNegativeButton(R.string.quit_button, new DialogInterface.OnClickListener() {
115 | public void onClick(DialogInterface dialog, int which) {
116 | finish();
117 | }
118 | }).create();
119 | }
120 |
121 | private void doCheck() {
122 | mCheckLicenseButton.setEnabled(false);
123 | setProgressBarIndeterminateVisibility(true);
124 | mStatusText.setText(R.string.checking_license);
125 | mChecker.checkAccess(mLicenseCheckerCallback);
126 | }
127 |
128 | private void displayResult(final String result) {
129 | mHandler.post(new Runnable() {
130 | public void run() {
131 | mStatusText.setText(result);
132 | setProgressBarIndeterminateVisibility(false);
133 | mCheckLicenseButton.setEnabled(true);
134 | }
135 | });
136 | }
137 |
138 | private void displayDialog(final boolean showRetry) {
139 | mHandler.post(new Runnable() {
140 | public void run() {
141 | setProgressBarIndeterminateVisibility(false);
142 | showDialog(showRetry ? 1 : 0);
143 | mCheckLicenseButton.setEnabled(true);
144 | }
145 | });
146 | }
147 |
148 | private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
149 | public void allow(int policyReason) {
150 | if (isFinishing()) {
151 | // Don't update UI if Activity is finishing.
152 | return;
153 | }
154 | // Should allow user access.
155 | displayResult(getString(R.string.allow));
156 | }
157 |
158 | public void dontAllow(int policyReason) {
159 | if (isFinishing()) {
160 | // Don't update UI if Activity is finishing.
161 | return;
162 | }
163 | displayResult(getString(R.string.dont_allow));
164 | // Should not allow access. In most cases, the app should assume
165 | // the user has access unless it encounters this. If it does,
166 | // the app should inform the user of their unlicensed ways
167 | // and then either shut down the app or limit the user to a
168 | // restricted set of features.
169 | // In this example, we show a dialog that takes the user to a deep
170 | // link returned by the license checker.
171 | // If the reason for the lack of license is that the service is
172 | // unavailable or there is another problem, we display a
173 | // retry button on the dialog and a different message.
174 | displayDialog(policyReason == Policy.RETRY);
175 | }
176 |
177 | public void applicationError(int errorCode) {
178 | if (isFinishing()) {
179 | // Don't update UI if Activity is finishing.
180 | return;
181 | }
182 | // This is a polite way of saying the developer made a mistake
183 | // while setting up or calling the license checker library.
184 | // Please examine the error code and fix the error.
185 | String result = String.format(getString(R.string.application_error), errorCode);
186 | displayResult(result);
187 | }
188 | }
189 |
190 | @Override
191 | protected void onDestroy() {
192 | super.onDestroy();
193 | mChecker.onDestroy();
194 | }
195 |
196 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/LicenseValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | import com.google.android.vending.licensing.util.Base64;
20 | import com.google.android.vending.licensing.util.Base64DecoderException;
21 |
22 | import android.text.TextUtils;
23 | import android.util.Log;
24 |
25 | import java.security.InvalidKeyException;
26 | import java.security.NoSuchAlgorithmException;
27 | import java.security.PublicKey;
28 | import java.security.Signature;
29 | import java.security.SignatureException;
30 |
31 | /**
32 | * Contains data related to a licensing request and methods to verify
33 | * and process the response.
34 | */
35 | class LicenseValidator {
36 | private static final String TAG = "LicenseValidator";
37 |
38 | // Server response codes.
39 | private static final int LICENSED = 0x0;
40 | private static final int NOT_LICENSED = 0x1;
41 | private static final int LICENSED_OLD_KEY = 0x2;
42 | private static final int ERROR_NOT_MARKET_MANAGED = 0x3;
43 | private static final int ERROR_SERVER_FAILURE = 0x4;
44 | private static final int ERROR_OVER_QUOTA = 0x5;
45 |
46 | private static final int ERROR_CONTACTING_SERVER = 0x101;
47 | private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
48 | private static final int ERROR_NON_MATCHING_UID = 0x103;
49 |
50 | private final Policy mPolicy;
51 | private final LicenseCheckerCallback mCallback;
52 | private final int mNonce;
53 | private final String mPackageName;
54 | private final String mVersionCode;
55 | private final DeviceLimiter mDeviceLimiter;
56 |
57 | LicenseValidator(Policy policy, DeviceLimiter deviceLimiter, LicenseCheckerCallback callback,
58 | int nonce, String packageName, String versionCode) {
59 | mPolicy = policy;
60 | mDeviceLimiter = deviceLimiter;
61 | mCallback = callback;
62 | mNonce = nonce;
63 | mPackageName = packageName;
64 | mVersionCode = versionCode;
65 | }
66 |
67 | public LicenseCheckerCallback getCallback() {
68 | return mCallback;
69 | }
70 |
71 | public int getNonce() {
72 | return mNonce;
73 | }
74 |
75 | public String getPackageName() {
76 | return mPackageName;
77 | }
78 |
79 | private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
80 |
81 | /**
82 | * Verifies the response from server and calls appropriate callback method.
83 | *
84 | * @param publicKey public key associated with the developer account
85 | * @param responseCode server response code
86 | * @param signedData signed data from server
87 | * @param signature server signature
88 | */
89 | public void verify(PublicKey publicKey, int responseCode, String signedData, String signature) {
90 | String userId = null;
91 | // Skip signature check for unsuccessful requests
92 | ResponseData data = null;
93 | if (responseCode == LICENSED || responseCode == NOT_LICENSED ||
94 | responseCode == LICENSED_OLD_KEY) {
95 | // Verify signature.
96 | try {
97 | if (TextUtils.isEmpty(signedData)) {
98 | Log.e(TAG, "Signature verification failed: signedData is empty. " +
99 | "(Device not signed-in to any Google accounts?)");
100 | handleInvalidResponse();
101 | return;
102 | }
103 |
104 | Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
105 | sig.initVerify(publicKey);
106 | sig.update(signedData.getBytes());
107 |
108 | if (!sig.verify(Base64.decode(signature))) {
109 | Log.e(TAG, "Signature verification failed.");
110 | handleInvalidResponse();
111 | return;
112 | }
113 | } catch (NoSuchAlgorithmException e) {
114 | // This can't happen on an Android compatible device.
115 | throw new RuntimeException(e);
116 | } catch (InvalidKeyException e) {
117 | handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PUBLIC_KEY);
118 | return;
119 | } catch (SignatureException e) {
120 | throw new RuntimeException(e);
121 | } catch (Base64DecoderException e) {
122 | Log.e(TAG, "Could not Base64-decode signature.");
123 | handleInvalidResponse();
124 | return;
125 | }
126 |
127 | // Parse and validate response.
128 | try {
129 | data = ResponseData.parse(signedData);
130 | } catch (IllegalArgumentException e) {
131 | Log.e(TAG, "Could not parse response.");
132 | handleInvalidResponse();
133 | return;
134 | }
135 |
136 | if (data.responseCode != responseCode) {
137 | Log.e(TAG, "Response codes don't match.");
138 | handleInvalidResponse();
139 | return;
140 | }
141 |
142 | if (data.nonce != mNonce) {
143 | Log.e(TAG, "Nonce doesn't match.");
144 | handleInvalidResponse();
145 | return;
146 | }
147 |
148 | if (!data.packageName.equals(mPackageName)) {
149 | Log.e(TAG, "Package name doesn't match.");
150 | handleInvalidResponse();
151 | return;
152 | }
153 |
154 | if (!data.versionCode.equals(mVersionCode)) {
155 | Log.e(TAG, "Version codes don't match.");
156 | handleInvalidResponse();
157 | return;
158 | }
159 |
160 | // Application-specific user identifier.
161 | userId = data.userId;
162 | if (TextUtils.isEmpty(userId)) {
163 | Log.e(TAG, "User identifier is empty.");
164 | handleInvalidResponse();
165 | return;
166 | }
167 | }
168 |
169 | switch (responseCode) {
170 | case LICENSED:
171 | case LICENSED_OLD_KEY:
172 | int limiterResponse = mDeviceLimiter.isDeviceAllowed(userId);
173 | handleResponse(limiterResponse, data);
174 | break;
175 | case NOT_LICENSED:
176 | handleResponse(Policy.NOT_LICENSED, data);
177 | break;
178 | case ERROR_CONTACTING_SERVER:
179 | Log.w(TAG, "Error contacting licensing server.");
180 | handleResponse(Policy.RETRY, data);
181 | break;
182 | case ERROR_SERVER_FAILURE:
183 | Log.w(TAG, "An error has occurred on the licensing server.");
184 | handleResponse(Policy.RETRY, data);
185 | break;
186 | case ERROR_OVER_QUOTA:
187 | Log.w(TAG, "Licensing server is refusing to talk to this device, over quota.");
188 | handleResponse(Policy.RETRY, data);
189 | break;
190 | case ERROR_INVALID_PACKAGE_NAME:
191 | handleApplicationError(LicenseCheckerCallback.ERROR_INVALID_PACKAGE_NAME);
192 | break;
193 | case ERROR_NON_MATCHING_UID:
194 | handleApplicationError(LicenseCheckerCallback.ERROR_NON_MATCHING_UID);
195 | break;
196 | case ERROR_NOT_MARKET_MANAGED:
197 | handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED);
198 | break;
199 | default:
200 | Log.e(TAG, "Unknown response code for license check.");
201 | handleInvalidResponse();
202 | }
203 | }
204 |
205 | /**
206 | * Confers with policy and calls appropriate callback method.
207 | *
208 | * @param response
209 | * @param rawData
210 | */
211 | private void handleResponse(int response, ResponseData rawData) {
212 | // Update policy data and increment retry counter (if needed)
213 | mPolicy.processServerResponse(response, rawData);
214 |
215 | // Given everything we know, including cached data, ask the policy if we should grant
216 | // access.
217 | if (mPolicy.allowAccess()) {
218 | mCallback.allow(response);
219 | } else {
220 | mCallback.dontAllow(response);
221 | }
222 | }
223 |
224 | private void handleApplicationError(int code) {
225 | mCallback.applicationError(code);
226 | }
227 |
228 | private void handleInvalidResponse() {
229 | mCallback.dontAllow(Policy.NOT_LICENSED);
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/ServerManagedPolicy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.android.vending.licensing;
18 |
19 | import java.net.URI;
20 | import java.net.URISyntaxException;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 |
24 | import android.content.Context;
25 | import android.content.SharedPreferences;
26 | import android.util.Log;
27 |
28 | import com.google.android.vending.licensing.util.URIQueryDecoder;
29 |
30 | /**
31 | * Default policy. All policy decisions are based off of response data received
32 | * from the licensing service. Specifically, the licensing server sends the
33 | * following information: response validity period, error retry period,
34 | * error retry count and a URL for restoring app access in unlicensed cases.
35 | *
36 | * These values will vary based on the the way the application is configured in
37 | * the Google Play publishing console, such as whether the application is
38 | * marked as free or is within its refund period, as well as how often an
39 | * application is checking with the licensing service.
40 | *
41 | * Developers who need more fine grained control over their application's
42 | * licensing policy should implement a custom Policy.
43 | */
44 | public class ServerManagedPolicy implements Policy {
45 |
46 | private static final String TAG = "ServerManagedPolicy";
47 | private static final String PREFS_FILE = "com.google.android.vending.licensing.ServerManagedPolicy";
48 | private static final String PREF_LAST_RESPONSE = "lastResponse";
49 | private static final String PREF_VALIDITY_TIMESTAMP = "validityTimestamp";
50 | private static final String PREF_RETRY_UNTIL = "retryUntil";
51 | private static final String PREF_MAX_RETRIES = "maxRetries";
52 | private static final String PREF_RETRY_COUNT = "retryCount";
53 | private static final String PREF_LICENSING_URL = "licensingUrl";
54 | private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
55 | private static final String DEFAULT_RETRY_UNTIL = "0";
56 | private static final String DEFAULT_MAX_RETRIES = "0";
57 | private static final String DEFAULT_RETRY_COUNT = "0";
58 |
59 | private static final long MILLIS_PER_MINUTE = 60 * 1000;
60 |
61 | private long mValidityTimestamp;
62 | private long mRetryUntil;
63 | private long mMaxRetries;
64 | private long mRetryCount;
65 | private long mLastResponseTime = 0;
66 | private int mLastResponse;
67 | private String mLicensingUrl;
68 | private PreferenceObfuscator mPreferences;
69 |
70 | /**
71 | * @param context The context for the current application
72 | * @param obfuscator An obfuscator to be used with preferences.
73 | */
74 | public ServerManagedPolicy(Context context, Obfuscator obfuscator) {
75 | // Import old values
76 | SharedPreferences sp = context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
77 | mPreferences = new PreferenceObfuscator(sp, obfuscator);
78 | mLastResponse = Integer.parseInt(
79 | mPreferences.getString(PREF_LAST_RESPONSE, Integer.toString(Policy.RETRY)));
80 | mValidityTimestamp = Long.parseLong(mPreferences.getString(PREF_VALIDITY_TIMESTAMP,
81 | DEFAULT_VALIDITY_TIMESTAMP));
82 | mRetryUntil = Long.parseLong(mPreferences.getString(PREF_RETRY_UNTIL, DEFAULT_RETRY_UNTIL));
83 | mMaxRetries = Long.parseLong(mPreferences.getString(PREF_MAX_RETRIES, DEFAULT_MAX_RETRIES));
84 | mRetryCount = Long.parseLong(mPreferences.getString(PREF_RETRY_COUNT, DEFAULT_RETRY_COUNT));
85 | mLicensingUrl = mPreferences.getString(PREF_LICENSING_URL, null);
86 | }
87 |
88 | /**
89 | * Process a new response from the license server.
90 | *
91 | * This data will be used for computing future policy decisions. The
92 | * following parameters are processed:
93 | *
39 | * These values will vary based on the the way the application is configured in
40 | * the Google Play publishing console, such as whether the application is
41 | * marked as free or is within its refund period, as well as how often an
42 | * application is checking with the licensing service.
43 | *
44 | * Developers who need more fine grained control over their application's
45 | * licensing policy should implement a custom Policy.
46 | */
47 | public class APKExpansionPolicy implements Policy {
48 |
49 | private static final String TAG = "APKExpansionPolicy";
50 | private static final String PREFS_FILE = "com.google.android.vending.licensing.APKExpansionPolicy";
51 | private static final String PREF_LAST_RESPONSE = "lastResponse";
52 | private static final String PREF_VALIDITY_TIMESTAMP = "validityTimestamp";
53 | private static final String PREF_RETRY_UNTIL = "retryUntil";
54 | private static final String PREF_MAX_RETRIES = "maxRetries";
55 | private static final String PREF_RETRY_COUNT = "retryCount";
56 | private static final String PREF_LICENSING_URL = "licensingUrl";
57 | private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
58 | private static final String DEFAULT_RETRY_UNTIL = "0";
59 | private static final String DEFAULT_MAX_RETRIES = "0";
60 | private static final String DEFAULT_RETRY_COUNT = "0";
61 |
62 | private static final long MILLIS_PER_MINUTE = 60 * 1000;
63 |
64 | private long mValidityTimestamp;
65 | private long mRetryUntil;
66 | private long mMaxRetries;
67 | private long mRetryCount;
68 | private long mLastResponseTime = 0;
69 | private int mLastResponse;
70 | private String mLicensingUrl;
71 | private PreferenceObfuscator mPreferences;
72 | private Vector
118 | * This data will be used for computing future policy decisions. The
119 | * following parameters are processed:
120 | *
53 | * The LicenseChecker is configured via a {@link Policy} which contains the logic to determine
54 | * whether a user should have access to the application. For example, the Policy can define a
55 | * threshold for allowable number of server or client failures before the library reports the user
56 | * as not having access.
57 | *
58 | * Must also provide the Base64-encoded RSA public key associated with your developer account. The
59 | * public key is obtainable from the publisher site.
60 | */
61 | public class LicenseChecker implements ServiceConnection {
62 | private static final String TAG = "LicenseChecker";
63 |
64 | private static final String KEY_FACTORY_ALGORITHM = "RSA";
65 |
66 | // Timeout value (in milliseconds) for calls to service.
67 | private static final int TIMEOUT_MS = 10 * 1000;
68 |
69 | private static final SecureRandom RANDOM = new SecureRandom();
70 | private static final boolean DEBUG_LICENSE_ERROR = false;
71 |
72 | private ILicensingService mService;
73 |
74 | private PublicKey mPublicKey;
75 | private final Context mContext;
76 | private final Policy mPolicy;
77 | /**
78 | * A handler for running tasks on a background thread. We don't want license processing to block
79 | * the UI thread.
80 | */
81 | private Handler mHandler;
82 | private final String mPackageName;
83 | private final String mVersionCode;
84 | private final Set
131 | * NOTE: This call uses a trivially obfuscated string (base64-encoded). For best security, we
132 | * recommend obfuscating the string that is passed into bindService using another method of your
133 | * own devising.
134 | *
135 | * source string: "com.android.vending.licensing.ILicensingService"
136 | *
137 | *
138 | * @param callback
139 | */
140 | public synchronized void checkAccess(LicenseCheckerCallback callback) {
141 | // If we have a valid recent LICENSED response, we can skip asking
142 | // Market.
143 | if (mPolicy.allowAccess()) {
144 | Log.i(TAG, "Using cached license response");
145 | callback.allow(Policy.LICENSED);
146 | } else {
147 | LicenseValidator validator = new LicenseValidator(mPolicy, new NullDeviceLimiter(),
148 | callback, generateNonce(), mPackageName, mVersionCode);
149 |
150 | if (mService == null) {
151 | Log.i(TAG, "Binding to licensing service.");
152 | try {
153 | boolean bindResult = mContext
154 | .bindService(
155 | new Intent(
156 | new String(
157 | // Base64 encoded -
158 | // com.android.vending.licensing.ILicensingService
159 | // Consider encoding this in another way in your
160 | // code to improve security
161 | Base64.decode(
162 | "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))
163 | // As of Android 5.0, implicit
164 | // Service Intents are no longer
165 | // allowed because it's not
166 | // possible for the user to
167 | // participate in disambiguating
168 | // them. This does mean we break
169 | // compatibility with Android
170 | // Cupcake devices with this
171 | // release, since setPackage was
172 | // added in Donut.
173 | .setPackage(
174 | new String(
175 | // Base64
176 | // encoded -
177 | // com.android.vending
178 | Base64.decode(
179 | "Y29tLmFuZHJvaWQudmVuZGluZw=="))),
180 | this, // ServiceConnection.
181 | Context.BIND_AUTO_CREATE);
182 | if (bindResult) {
183 | mPendingChecks.offer(validator);
184 | } else {
185 | Log.e(TAG, "Could not bind to service.");
186 | handleServiceConnectionError(validator);
187 | }
188 | } catch (SecurityException e) {
189 | callback.applicationError(LicenseCheckerCallback.ERROR_MISSING_PERMISSION);
190 | } catch (Base64DecoderException e) {
191 | e.printStackTrace();
192 | }
193 | } else {
194 | mPendingChecks.offer(validator);
195 | runChecks();
196 | }
197 | }
198 | }
199 |
200 | /**
201 | * Triggers the last deep link licensing URL returned from the server, which redirects users to a
202 | * page which enables them to gain access to the app. If no such URL is returned by the server, it
203 | * will go to the details page of the app in the Play Store.
204 | */
205 | public void followLastLicensingUrl(Context context) {
206 | String licensingUrl = mPolicy.getLicensingUrl();
207 | if (licensingUrl == null) {
208 | licensingUrl = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
209 | }
210 | Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(licensingUrl));
211 | marketIntent.setPackage("com.android.vending");
212 | if (!(context instanceof Activity)) {
213 | marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
214 | }
215 | context.startActivity(marketIntent);
216 | }
217 |
218 | private void runChecks() {
219 | LicenseValidator validator;
220 | while ((validator = mPendingChecks.poll()) != null) {
221 | try {
222 | Log.i(TAG, "Calling checkLicense on service for " + validator.getPackageName());
223 | mService.checkLicense(
224 | validator.getNonce(), validator.getPackageName(),
225 | new ResultListener(validator));
226 | mChecksInProgress.add(validator);
227 | } catch (RemoteException e) {
228 | Log.w(TAG, "RemoteException in checkLicense call.", e);
229 | handleServiceConnectionError(validator);
230 | }
231 | }
232 | }
233 |
234 | private synchronized void finishCheck(LicenseValidator validator) {
235 | mChecksInProgress.remove(validator);
236 | if (mChecksInProgress.isEmpty()) {
237 | cleanupService();
238 | }
239 | }
240 |
241 | private class ResultListener extends ILicenseResultListener.Stub {
242 | private final LicenseValidator mValidator;
243 | private Runnable mOnTimeout;
244 |
245 | public ResultListener(LicenseValidator validator) {
246 | mValidator = validator;
247 | mOnTimeout = new Runnable() {
248 | public void run() {
249 | Log.i(TAG, "Check timed out.");
250 | handleServiceConnectionError(mValidator);
251 | finishCheck(mValidator);
252 | }
253 | };
254 | startTimeout();
255 | }
256 |
257 | private static final int ERROR_CONTACTING_SERVER = 0x101;
258 | private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
259 | private static final int ERROR_NON_MATCHING_UID = 0x103;
260 |
261 | // Runs in IPC thread pool. Post it to the Handler, so we can guarantee
262 | // either this or the timeout runs.
263 | public void verifyLicense(final int responseCode, final String signedData,
264 | final String signature) {
265 | mHandler.post(new Runnable() {
266 | public void run() {
267 | Log.i(TAG, "Received response.");
268 | // Make sure it hasn't already timed out.
269 | if (mChecksInProgress.contains(mValidator)) {
270 | clearTimeout();
271 | mValidator.verify(mPublicKey, responseCode, signedData, signature);
272 | finishCheck(mValidator);
273 | }
274 | if (DEBUG_LICENSE_ERROR) {
275 | boolean logResponse;
276 | String stringError = null;
277 | switch (responseCode) {
278 | case ERROR_CONTACTING_SERVER:
279 | logResponse = true;
280 | stringError = "ERROR_CONTACTING_SERVER";
281 | break;
282 | case ERROR_INVALID_PACKAGE_NAME:
283 | logResponse = true;
284 | stringError = "ERROR_INVALID_PACKAGE_NAME";
285 | break;
286 | case ERROR_NON_MATCHING_UID:
287 | logResponse = true;
288 | stringError = "ERROR_NON_MATCHING_UID";
289 | break;
290 | default:
291 | logResponse = false;
292 | }
293 |
294 | if (logResponse) {
295 | String android_id = Secure.getString(mContext.getContentResolver(),
296 | Secure.ANDROID_ID);
297 | Date date = new Date();
298 | Log.d(TAG, "Server Failure: " + stringError);
299 | Log.d(TAG, "Android ID: " + android_id);
300 | Log.d(TAG, "Time: " + date.toGMTString());
301 | }
302 | }
303 |
304 | }
305 | });
306 | }
307 |
308 | private void startTimeout() {
309 | Log.i(TAG, "Start monitoring timeout.");
310 | mHandler.postDelayed(mOnTimeout, TIMEOUT_MS);
311 | }
312 |
313 | private void clearTimeout() {
314 | Log.i(TAG, "Clearing timeout.");
315 | mHandler.removeCallbacks(mOnTimeout);
316 | }
317 | }
318 |
319 | public synchronized void onServiceConnected(ComponentName name, IBinder service) {
320 | mService = ILicensingService.Stub.asInterface(service);
321 | runChecks();
322 | }
323 |
324 | public synchronized void onServiceDisconnected(ComponentName name) {
325 | // Called when the connection with the service has been
326 | // unexpectedly disconnected. That is, Market crashed.
327 | // If there are any checks in progress, the timeouts will handle them.
328 | Log.w(TAG, "Service unexpectedly disconnected.");
329 | mService = null;
330 | }
331 |
332 | /**
333 | * Generates policy response for service connection errors, as a result of disconnections or
334 | * timeouts.
335 | */
336 | private synchronized void handleServiceConnectionError(LicenseValidator validator) {
337 | mPolicy.processServerResponse(Policy.RETRY, null);
338 |
339 | if (mPolicy.allowAccess()) {
340 | validator.getCallback().allow(Policy.RETRY);
341 | } else {
342 | validator.getCallback().dontAllow(Policy.RETRY);
343 | }
344 | }
345 |
346 | /** Unbinds service if necessary and removes reference to it. */
347 | private void cleanupService() {
348 | if (mService != null) {
349 | try {
350 | mContext.unbindService(this);
351 | } catch (IllegalArgumentException e) {
352 | // Somehow we've already been unbound. This is a non-fatal
353 | // error.
354 | Log.e(TAG, "Unable to unbind from licensing service (already unbound)");
355 | }
356 | mService = null;
357 | }
358 | }
359 |
360 | /**
361 | * Inform the library that the context is about to be destroyed, so that any open connections
362 | * can be cleaned up.
363 | *
364 | * Failure to call this method can result in a crash under certain circumstances, such as during
365 | * screen rotation if an Activity requests the license check or when the user exits the
366 | * application.
367 | */
368 | public synchronized void onDestroy() {
369 | cleanupService();
370 | mHandler.getLooper().quit();
371 | }
372 |
373 | /** Generates a nonce (number used once). */
374 | private int generateNonce() {
375 | return RANDOM.nextInt();
376 | }
377 |
378 | /**
379 | * Get version code for the application package name.
380 | *
381 | * @param context
382 | * @param packageName application package name
383 | * @return the version code or empty string if package not found
384 | */
385 | private static String getVersionCode(Context context, String packageName) {
386 | try {
387 | return String.valueOf(
388 | context.getPackageManager().getPackageInfo(packageName, 0).versionCode);
389 | } catch (NameNotFoundException e) {
390 | Log.e(TAG, "Package not found. could not get version code.");
391 | return "";
392 | }
393 | }
394 | }
395 |
--------------------------------------------------------------------------------
/lvl_library/src/main/java/com/google/android/vending/licensing/util/Base64.java:
--------------------------------------------------------------------------------
1 | // Portions copyright 2002, Google, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.google.android.vending.licensing.util;
16 |
17 | // This code was converted from code at http://iharder.sourceforge.net/base64/
18 | // Lots of extraneous features were removed.
19 | /* The original code said:
20 | *
21 | * I am placing this code in the Public Domain. Do with it as you will.
22 | * This software comes with no guarantees or warranties but with
23 | * plenty of well-wishing instead!
24 | * Please visit
25 | * http://iharder.net/xmlizable
26 | * periodically to check for updates or to contribute improvements.
27 | * Note {@link CharBase64} is a GWT-compatible implementation of this
39 | * class.
40 | */
41 | public class Base64 {
42 | /** Specify encoding (value is {@code true}). */
43 | public final static boolean ENCODE = true;
44 |
45 | /** Specify decoding (value is {@code false}). */
46 | public final static boolean DECODE = false;
47 |
48 | /** The equals sign (=) as a byte. */
49 | private final static byte EQUALS_SIGN = (byte) '=';
50 |
51 | /** The new line character (\n) as a byte. */
52 | private final static byte NEW_LINE = (byte) '\n';
53 |
54 | /**
55 | * The 64 valid Base64 values.
56 | */
57 | private final static byte[] ALPHABET =
58 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
59 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
60 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
61 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
62 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
63 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
64 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
65 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
66 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
67 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
68 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
69 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
70 | (byte) '9', (byte) '+', (byte) '/'};
71 |
72 | /**
73 | * The 64 valid web safe Base64 values.
74 | */
75 | private final static byte[] WEBSAFE_ALPHABET =
76 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
77 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
78 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
79 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
80 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
81 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
82 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
83 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
84 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
85 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
86 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
87 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
88 | (byte) '9', (byte) '-', (byte) '_'};
89 |
90 | /**
91 | * Translates a Base64 value to either its 6-bit reconstruction value
92 | * or a negative number indicating some other meaning.
93 | **/
94 | private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
95 | -5, -5, // Whitespace: Tab and Linefeed
96 | -9, -9, // Decimal 11 - 12
97 | -5, // Whitespace: Carriage Return
98 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
99 | -9, -9, -9, -9, -9, // Decimal 27 - 31
100 | -5, // Whitespace: Space
101 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
102 | 62, // Plus sign at decimal 43
103 | -9, -9, -9, // Decimal 44 - 46
104 | 63, // Slash at decimal 47
105 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
106 | -9, -9, -9, // Decimal 58 - 60
107 | -1, // Equals sign at decimal 61
108 | -9, -9, -9, // Decimal 62 - 64
109 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
110 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
111 | -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
112 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
113 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
114 | -9, -9, -9, -9, -9 // Decimal 123 - 127
115 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
116 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
117 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
118 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
119 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
120 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
121 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
122 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
123 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
124 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
125 | };
126 |
127 | /** The web safe decodabet */
128 | private final static byte[] WEBSAFE_DECODABET =
129 | {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
130 | -5, -5, // Whitespace: Tab and Linefeed
131 | -9, -9, // Decimal 11 - 12
132 | -5, // Whitespace: Carriage Return
133 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
134 | -9, -9, -9, -9, -9, // Decimal 27 - 31
135 | -5, // Whitespace: Space
136 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
137 | 62, // Dash '-' sign at decimal 45
138 | -9, -9, // Decimal 46-47
139 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
140 | -9, -9, -9, // Decimal 58 - 60
141 | -1, // Equals sign at decimal 61
142 | -9, -9, -9, // Decimal 62 - 64
143 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
144 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
145 | -9, -9, -9, -9, // Decimal 91-94
146 | 63, // Underscore '_' at decimal 95
147 | -9, // Decimal 96
148 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
149 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
150 | -9, -9, -9, -9, -9 // Decimal 123 - 127
151 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
152 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
153 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
154 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
155 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
156 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
157 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
158 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
159 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
160 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
161 | };
162 |
163 | // Indicates white space in encoding
164 | private final static byte WHITE_SPACE_ENC = -5;
165 | // Indicates equals sign in encoding
166 | private final static byte EQUALS_SIGN_ENC = -1;
167 |
168 | /** Defeats instantiation. */
169 | private Base64() {
170 | }
171 |
172 | /* ******** E N C O D I N G M E T H O D S ******** */
173 |
174 | /**
175 | * Encodes up to three bytes of the array source
176 | * and writes the resulting four Base64 bytes to destination.
177 | * The source and destination arrays can be manipulated
178 | * anywhere along their length by specifying
179 | * srcOffset and destOffset.
180 | * This method does not check to make sure your arrays
181 | * are large enough to accommodate srcOffset + 3 for
182 | * the source array or destOffset + 4 for
183 | * the destination array.
184 | * The actual number of significant bytes in your array is
185 | * given by numSigBytes.
186 | *
187 | * @param source the array to convert
188 | * @param srcOffset the index where conversion begins
189 | * @param numSigBytes the number of significant bytes in your array
190 | * @param destination the array to hold the conversion
191 | * @param destOffset the index where output will be put
192 | * @param alphabet is the encoding alphabet
193 | * @return the destination array
194 | * @since 1.3
195 | */
196 | private static byte[] encode3to4(byte[] source, int srcOffset,
197 | int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
198 | // 1 2 3
199 | // 01234567890123456789012345678901 Bit position
200 | // --------000000001111111122222222 Array position from threeBytes
201 | // --------| || || || | Six bit groups to index alphabet
202 | // >>18 >>12 >> 6 >> 0 Right shift necessary
203 | // 0x3f 0x3f 0x3f Additional AND
204 |
205 | // Create buffer with zero-padding if there are only one or two
206 | // significant bytes passed in the array.
207 | // We have to shift left 24 in order to flush out the 1's that appear
208 | // when Java treats a value as negative that is cast from a byte to an int.
209 | int inBuff =
210 | (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
211 | | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
212 | | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
213 |
214 | switch (numSigBytes) {
215 | case 3:
216 | destination[destOffset] = alphabet[(inBuff >>> 18)];
217 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
218 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
219 | destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
220 | return destination;
221 | case 2:
222 | destination[destOffset] = alphabet[(inBuff >>> 18)];
223 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
224 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
225 | destination[destOffset + 3] = EQUALS_SIGN;
226 | return destination;
227 | case 1:
228 | destination[destOffset] = alphabet[(inBuff >>> 18)];
229 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
230 | destination[destOffset + 2] = EQUALS_SIGN;
231 | destination[destOffset + 3] = EQUALS_SIGN;
232 | return destination;
233 | default:
234 | return destination;
235 | } // end switch
236 | } // end encode3to4
237 |
238 | /**
239 | * Encodes a byte array into Base64 notation.
240 | * Equivalent to calling
241 | * {@code encodeBytes(source, 0, source.length)}
242 | *
243 | * @param source The data to convert
244 | * @since 1.4
245 | */
246 | public static String encode(byte[] source) {
247 | return encode(source, 0, source.length, ALPHABET, true);
248 | }
249 |
250 | /**
251 | * Encodes a byte array into web safe Base64 notation.
252 | *
253 | * @param source The data to convert
254 | * @param doPadding is {@code true} to pad result with '=' chars
255 | * if it does not fall on 3 byte boundaries
256 | */
257 | public static String encodeWebSafe(byte[] source, boolean doPadding) {
258 | return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
259 | }
260 |
261 | /**
262 | * Encodes a byte array into Base64 notation.
263 | *
264 | * @param source The data to convert
265 | * @param off Offset in array where conversion should begin
266 | * @param len Length of data to convert
267 | * @param alphabet is the encoding alphabet
268 | * @param doPadding is {@code true} to pad result with '=' chars
269 | * if it does not fall on 3 byte boundaries
270 | * @since 1.4
271 | */
272 | public static String encode(byte[] source, int off, int len, byte[] alphabet,
273 | boolean doPadding) {
274 | byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
275 | int outLen = outBuff.length;
276 |
277 | // If doPadding is false, set length to truncate '='
278 | // padding characters
279 | while (doPadding == false && outLen > 0) {
280 | if (outBuff[outLen - 1] != '=') {
281 | break;
282 | }
283 | outLen -= 1;
284 | }
285 |
286 | return new String(outBuff, 0, outLen);
287 | }
288 |
289 | /**
290 | * Encodes a byte array into Base64 notation.
291 | *
292 | * @param source The data to convert
293 | * @param off Offset in array where conversion should begin
294 | * @param len Length of data to convert
295 | * @param alphabet is the encoding alphabet
296 | * @param maxLineLength maximum length of one line.
297 | * @return the BASE64-encoded byte array
298 | */
299 | public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
300 | int maxLineLength) {
301 | int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
302 | int len43 = lenDiv3 * 4;
303 | byte[] outBuff = new byte[len43 // Main 4:3
304 | + (len43 / maxLineLength)]; // New lines
305 |
306 | int d = 0;
307 | int e = 0;
308 | int len2 = len - 2;
309 | int lineLength = 0;
310 | for (; d < len2; d += 3, e += 4) {
311 |
312 | // The following block of code is the same as
313 | // encode3to4( source, d + off, 3, outBuff, e, alphabet );
314 | // but inlined for faster encoding (~20% improvement)
315 | int inBuff =
316 | ((source[d + off] << 24) >>> 8)
317 | | ((source[d + 1 + off] << 24) >>> 16)
318 | | ((source[d + 2 + off] << 24) >>> 24);
319 | outBuff[e] = alphabet[(inBuff >>> 18)];
320 | outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
321 | outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
322 | outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
323 |
324 | lineLength += 4;
325 | if (lineLength == maxLineLength) {
326 | outBuff[e + 4] = NEW_LINE;
327 | e++;
328 | lineLength = 0;
329 | } // end if: end of line
330 | } // end for: each piece of array
331 |
332 | if (d < len) {
333 | encode3to4(source, d + off, len - d, outBuff, e, alphabet);
334 |
335 | lineLength += 4;
336 | if (lineLength == maxLineLength) {
337 | // Add a last newline
338 | outBuff[e + 4] = NEW_LINE;
339 | e++;
340 | }
341 | e += 4;
342 | }
343 |
344 | assert (e == outBuff.length);
345 | return outBuff;
346 | }
347 |
348 |
349 | /* ******** D E C O D I N G M E T H O D S ******** */
350 |
351 |
352 | /**
353 | * Decodes four bytes from array source
354 | * and writes the resulting bytes (up to three of them)
355 | * to destination.
356 | * The source and destination arrays can be manipulated
357 | * anywhere along their length by specifying
358 | * srcOffset and destOffset.
359 | * This method does not check to make sure your arrays
360 | * are large enough to accommodate srcOffset + 4 for
361 | * the source array or destOffset + 3 for
362 | * the destination array.
363 | * This method returns the actual number of bytes that
364 | * were converted from the Base64 encoding.
365 | *
366 | *
367 | * @param source the array to convert
368 | * @param srcOffset the index where conversion begins
369 | * @param destination the array to hold the conversion
370 | * @param destOffset the index where output will be put
371 | * @param decodabet the decodabet for decoding Base64 content
372 | * @return the number of decoded bytes converted
373 | * @since 1.3
374 | */
375 | private static int decode4to3(byte[] source, int srcOffset,
376 | byte[] destination, int destOffset, byte[] decodabet) {
377 | // Example: Dk==
378 | if (source[srcOffset + 2] == EQUALS_SIGN) {
379 | int outBuff =
380 | ((decodabet[source[srcOffset]] << 24) >>> 6)
381 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
382 |
383 | destination[destOffset] = (byte) (outBuff >>> 16);
384 | return 1;
385 | } else if (source[srcOffset + 3] == EQUALS_SIGN) {
386 | // Example: DkL=
387 | int outBuff =
388 | ((decodabet[source[srcOffset]] << 24) >>> 6)
389 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
390 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
391 |
392 | destination[destOffset] = (byte) (outBuff >>> 16);
393 | destination[destOffset + 1] = (byte) (outBuff >>> 8);
394 | return 2;
395 | } else {
396 | // Example: DkLE
397 | int outBuff =
398 | ((decodabet[source[srcOffset]] << 24) >>> 6)
399 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
400 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
401 | | ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
402 |
403 | destination[destOffset] = (byte) (outBuff >> 16);
404 | destination[destOffset + 1] = (byte) (outBuff >> 8);
405 | destination[destOffset + 2] = (byte) (outBuff);
406 | return 3;
407 | }
408 | } // end decodeToBytes
409 |
410 |
411 | /**
412 | * Decodes data from Base64 notation.
413 | *
414 | * @param s the string to decode (decoded in default encoding)
415 | * @return the decoded data
416 | * @since 1.4
417 | */
418 | public static byte[] decode(String s) throws Base64DecoderException {
419 | byte[] bytes = s.getBytes();
420 | return decode(bytes, 0, bytes.length);
421 | }
422 |
423 | /**
424 | * Decodes data from web safe Base64 notation.
425 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
426 | *
427 | * @param s the string to decode (decoded in default encoding)
428 | * @return the decoded data
429 | */
430 | public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
431 | byte[] bytes = s.getBytes();
432 | return decodeWebSafe(bytes, 0, bytes.length);
433 | }
434 |
435 | /**
436 | * Decodes Base64 content in byte array format and returns
437 | * the decoded byte array.
438 | *
439 | * @param source The Base64 encoded data
440 | * @return decoded data
441 | * @since 1.3
442 | * @throws Base64DecoderException
443 | */
444 | public static byte[] decode(byte[] source) throws Base64DecoderException {
445 | return decode(source, 0, source.length);
446 | }
447 |
448 | /**
449 | * Decodes web safe Base64 content in byte array format and returns
450 | * the decoded data.
451 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
452 | *
453 | * @param source the string to decode (decoded in default encoding)
454 | * @return the decoded data
455 | */
456 | public static byte[] decodeWebSafe(byte[] source)
457 | throws Base64DecoderException {
458 | return decodeWebSafe(source, 0, source.length);
459 | }
460 |
461 | /**
462 | * Decodes Base64 content in byte array format and returns
463 | * the decoded byte array.
464 | *
465 | * @param source The Base64 encoded data
466 | * @param off The offset of where to begin decoding
467 | * @param len The length of characters to decode
468 | * @return decoded data
469 | * @since 1.3
470 | * @throws Base64DecoderException
471 | */
472 | public static byte[] decode(byte[] source, int off, int len)
473 | throws Base64DecoderException {
474 | return decode(source, off, len, DECODABET);
475 | }
476 |
477 | /**
478 | * Decodes web safe Base64 content in byte array format and returns
479 | * the decoded byte array.
480 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
481 | *
482 | * @param source The Base64 encoded data
483 | * @param off The offset of where to begin decoding
484 | * @param len The length of characters to decode
485 | * @return decoded data
486 | */
487 | public static byte[] decodeWebSafe(byte[] source, int off, int len)
488 | throws Base64DecoderException {
489 | return decode(source, off, len, WEBSAFE_DECODABET);
490 | }
491 |
492 | /**
493 | * Decodes Base64 content using the supplied decodabet and returns
494 | * the decoded byte array.
495 | *
496 | * @param source The Base64 encoded data
497 | * @param off The offset of where to begin decoding
498 | * @param len The length of characters to decode
499 | * @param decodabet the decodabet for decoding Base64 content
500 | * @return decoded data
501 | */
502 | public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
503 | throws Base64DecoderException {
504 | int len34 = len * 3 / 4;
505 | byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
506 | int outBuffPosn = 0;
507 |
508 | byte[] b4 = new byte[4];
509 | int b4Posn = 0;
510 | int i = 0;
511 | byte sbiCrop = 0;
512 | byte sbiDecode = 0;
513 | for (i = 0; i < len; i++) {
514 | sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
515 | sbiDecode = decodabet[sbiCrop];
516 |
517 | if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
518 | if (sbiDecode >= EQUALS_SIGN_ENC) {
519 | // An equals sign (for padding) must not occur at position 0 or 1
520 | // and must be the last byte[s] in the encoded value
521 | if (sbiCrop == EQUALS_SIGN) {
522 | int bytesLeft = len - i;
523 | byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
524 | if (b4Posn == 0 || b4Posn == 1) {
525 | throw new Base64DecoderException(
526 | "invalid padding byte '=' at byte offset " + i);
527 | } else if ((b4Posn == 3 && bytesLeft > 2)
528 | || (b4Posn == 4 && bytesLeft > 1)) {
529 | throw new Base64DecoderException(
530 | "padding byte '=' falsely signals end of encoded value "
531 | + "at offset " + i);
532 | } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
533 | throw new Base64DecoderException(
534 | "encoded value has invalid trailing byte");
535 | }
536 | break;
537 | }
538 |
539 | b4[b4Posn++] = sbiCrop;
540 | if (b4Posn == 4) {
541 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
542 | b4Posn = 0;
543 | }
544 | }
545 | } else {
546 | throw new Base64DecoderException("Bad Base64 input character at " + i
547 | + ": " + source[i + off] + "(decimal)");
548 | }
549 | }
550 |
551 | // Because web safe encoding allows non padding base64 encodes, we
552 | // need to pad the rest of the b4 buffer with equal signs when
553 | // b4Posn != 0. There can be at most 2 equal signs at the end of
554 | // four characters, so the b4 buffer must have two or three
555 | // characters. This also catches the case where the input is
556 | // padded with EQUALS_SIGN
557 | if (b4Posn != 0) {
558 | if (b4Posn == 1) {
559 | throw new Base64DecoderException("single trailing character at offset "
560 | + (len - 1));
561 | }
562 | b4[b4Posn++] = EQUALS_SIGN;
563 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
564 | }
565 |
566 | byte[] out = new byte[outBuffPosn];
567 | System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
568 | return out;
569 | }
570 | }
571 |
--------------------------------------------------------------------------------
94 | *
101 | *
102 | * @param response the result from validating the server response
103 | * @param rawData the raw server response data
104 | */
105 | public void processServerResponse(int response, ResponseData rawData) {
106 |
107 | // Update retry counter
108 | if (response != Policy.RETRY) {
109 | setRetryCount(0);
110 | } else {
111 | setRetryCount(mRetryCount + 1);
112 | }
113 |
114 | // Update server policy data
115 | Map
261 | *
262 | *
266 | */
267 | public boolean allowAccess() {
268 | long ts = System.currentTimeMillis();
269 | if (mLastResponse == Policy.LICENSED) {
270 | // Check if the LICENSED response occurred within the validity timeout.
271 | if (ts <= mValidityTimestamp) {
272 | // Cached LICENSED response is still valid.
273 | return true;
274 | }
275 | } else if (mLastResponse == Policy.RETRY &&
276 | ts < mLastResponseTime + MILLIS_PER_MINUTE) {
277 | // Only allow access if we are within the retry period or we haven't used up our
278 | // max retries.
279 | return (ts <= mRetryUntil || mRetryCount <= mMaxRetries);
280 | }
281 | return false;
282 | }
283 |
284 | private Map
121 | *
128 | *
129 | * @param response the result from validating the server response
130 | * @param rawData the raw server response data
131 | */
132 | public void processServerResponse(int response,
133 | com.google.android.vending.licensing.ResponseData rawData) {
134 |
135 | // Update retry counter
136 | if (response != Policy.RETRY) {
137 | setRetryCount(0);
138 | } else {
139 | setRetryCount(mRetryCount + 1);
140 | }
141 |
142 | // Update server policy data
143 | Map
373 | *
374 | *
378 | */
379 | public boolean allowAccess() {
380 | long ts = System.currentTimeMillis();
381 | if (mLastResponse == Policy.LICENSED) {
382 | // Check if the LICENSED response occurred within the validity
383 | // timeout.
384 | if (ts <= mValidityTimestamp) {
385 | // Cached LICENSED response is still valid.
386 | return true;
387 | }
388 | } else if (mLastResponse == Policy.RETRY &&
389 | ts < mLastResponseTime + MILLIS_PER_MINUTE) {
390 | // Only allow access if we are within the retry period or we haven't
391 | // used up our
392 | // max retries.
393 | return (ts <= mRetryUntil || mRetryCount <= mMaxRetries);
394 | }
395 | return false;
396 | }
397 |
398 | private Map