title: SafetyNet: package name always return null
body: <p>I am implementing SafetyNet API based on <a href="https://github.com/googlesamples/android-play-safetynet" rel="nofollow noreferrer">Google SafetyNet sample</a> and <a href="https://github.com/scottyab/safetynethelper" rel="nofollow noreferrer">SafetyNet Helper</a></p>
  
  <p>Here is my working code. First part is processing code I used at <a href="https://github.com/googlesamples/android-play-safetynet/blob/master/android/SafetyNetSample/Application/src/main/java/com/example/android/safetynetsample/SafetyNetSampleFragment.java" rel="nofollow noreferrer">SafetyNetSampleFragment</a>:</p>
  
  <pre><code>import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.support.v4.app.FragmentActivity;
  import android.util.Base64;
  import android.util.Log;
  
  import com.google.android.gms.common.ConnectionResult;
  import com.google.android.gms.common.api.GoogleApiClient;
  import com.google.android.gms.common.api.ResultCallback;
  import com.google.android.gms.common.api.Status;
  import com.google.android.gms.safetynet.SafetyNet;
  import com.google.android.gms.safetynet.SafetyNetApi;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.security.SecureRandom;
  import java.util.Random;
  
  
  public class SafetyNetVerifier implements GoogleApiClient.OnConnectionFailedListener {
  
      private final Random mRandom = new SecureRandom();
      private String mResult;
      private GoogleApiClient mGoogleApiClient;
  
      private FragmentActivity activity;
  
      public SafetyNetVerifier(FragmentActivity activity) {
          this.activity = activity;
          buildGoogleApiClient();
          sendSafetyNetRequest();
      }
  
      private byte[] getRequestNonce(String data) {
          ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
          byte[] bytes = new byte[24];
          mRandom.nextBytes(bytes);
          try {
              byteStream.write(bytes);
              byteStream.write(data.getBytes());
          } catch (IOException e) {
              return null;
          }
  
          return byteStream.toByteArray();
      }
  
      protected synchronized void buildGoogleApiClient() {
          mGoogleApiClient = new GoogleApiClient.Builder(activity)
                  .addApi(SafetyNet.API)
                  .enableAutoManage(activity, this)
                  .build();
      }
  
      private void sendSafetyNetRequest() {
          Log.e("hqthao", "Sending SafetyNet API request.");
  
          String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
          byte[] nonce = getRequestNonce(nonceData);
  
          SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
                  .setResultCallback(new ResultCallback&lt;SafetyNetApi.AttestationResult&gt;() {
  
                      @Override
                      public void onResult(SafetyNetApi.AttestationResult result) {
                          Status status = result.getStatus();
                          if (status.isSuccess()) {
                              mResult = result.getJwsResult();
                              Log.e("hqthao", "Success! SafetyNet result:\n" + mResult + "\n");
                              SafetyNetResponse response = parseJsonWebSignature(mResult);
                              Log.e("hqthao", response.toString());
                          }
                      }
                  });
      }
  
      @Nullable
      private SafetyNetResponse parseJsonWebSignature(String jwsResult) {
          if (jwsResult == null) {
              return null;
          }
          //the JWT (JSON WEB TOKEN) is just a 3 base64 encoded parts concatenated by a . character
          final String[] jwtParts = jwsResult.split("\\.");
  
          if (jwtParts.length == 3) {
              //we're only really interested in the body/payload
              String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
  
              return SafetyNetResponse.parse(decodedPayload);
          } else {
              return null;
          }
      }
  
      @Override
      public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
          Log.e("hqthao", "Error connecting to Google Play Services." + connectionResult.getErrorMessage());
      }
  
  }
  </code></pre>
  
  <p>Here is the model <code>SafetyNetResponse</code> I copied from <a href="https://github.com/scottyab/safetynethelper/blob/master/safetynetlib/src/main/java/com/scottyab/safetynet/SafetyNetResponse.java" rel="nofollow noreferrer">SafetyNetResponse</a>:</p>
  
  <pre><code>import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.util.Log;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import java.util.Arrays;
  
  public class SafetyNetResponse {
  
      private static final String TAG = SafetyNetResponse.class.getSimpleName();
      private String nonce;
      private long timestampMs;
      private String apkPackageName;
      private String[] apkCertificateDigestSha256;
      private String apkDigestSha256;
      private boolean ctsProfileMatch;
      private boolean basicIntegrity;
  
      //forces the parse()
      private SafetyNetResponse() {
      }
  
      /**
       * @return BASE64 encoded
       */
      public String getNonce() {
          return nonce;
      }
  
      public long getTimestampMs() {
          return timestampMs;
      }
  
      /**
       * @return com.package.name.of.requesting.app
       */
      public String getApkPackageName() {
          return apkPackageName;
      }
  
      /**
       * SHA-256 hash of the certificate used to sign requesting app
       *
       * @return BASE64 encoded
       */
      public String[] getApkCertificateDigestSha256() {
          return apkCertificateDigestSha256;
      }
  
      /**
       * SHA-256 hash of the app's APK
       *
       * @return BASE64 encoded
       */
      public String getApkDigestSha256() {
          return apkDigestSha256;
      }
  
  
      /**
       * If the value of "ctsProfileMatch" is true, then the profile of the device running your app matches the profile of a device that has passed Android compatibility testing.
       *
       * @return
       */
      public boolean isCtsProfileMatch() {
          return ctsProfileMatch;
      }
  
      /**
       * If the value of "basicIntegrity" is true, then the device running your app likely wasn't tampered with, but the device has not necessarily passed Android compatibility testing.
       *
       * @return
       */
      public boolean isBasicIntegrity() {
          return basicIntegrity;
      }
  
      /**
       * Parse the JSON string into populated SafetyNetResponse object
       *
       * @param decodedJWTPayload JSON String (always a json string according to JWT spec)
       * @return populated SafetyNetResponse
       */
      @Nullable
      public static SafetyNetResponse parse(@NonNull String decodedJWTPayload) {
  
          Log.d(TAG, "decodedJWTPayload json:" + decodedJWTPayload);
  
          SafetyNetResponse response = new SafetyNetResponse();
          try {
              JSONObject root = new JSONObject(decodedJWTPayload);
              if (root.has("nonce")) {
                  response.nonce = root.getString("nonce");
              }
  
              if (root.has("apkCertificateDigestSha256")) {
                  JSONArray jsonArray = root.getJSONArray("apkCertificateDigestSha256");
                  if (jsonArray != null) {
                      String[] certDigests = new String[jsonArray.length()];
                      for (int i = 0; i &lt; jsonArray.length(); i++) {
                          certDigests[i] = jsonArray.getString(i);
                      }
                      response.apkCertificateDigestSha256 = certDigests;
                  }
              }
  
              if (root.has("apkDigestSha256")) {
                  response.apkDigestSha256 = root.getString("apkDigestSha256");
              }
  
              if (root.has("apkPackageName")) {
                  response.apkPackageName = root.getString("apkPackageName");
              }
  
              if (root.has("basicIntegrity")) {
                  response.basicIntegrity = root.getBoolean("basicIntegrity");
              }
  
              if (root.has("ctsProfileMatch")) {
                  response.ctsProfileMatch = root.getBoolean("ctsProfileMatch");
              }
  
              if (root.has("timestampMs")) {
                  response.timestampMs = root.getLong("timestampMs");
              }
  
              return response;
          } catch (JSONException e) {
              Log.e(TAG, "problem parsing decodedJWTPayload:" + e.getMessage(), e);
          }
          return null;
      }
  
  
      @Override
      public String toString() {
          return "SafetyNetResponse{" +
                  "nonce='" + nonce + '\'' +
                  ", timestampMs=" + timestampMs +
                  ", apkPackageName='" + apkPackageName + '\'' +
                  ", apkCertificateDigestSha256=" + Arrays.toString(apkCertificateDigestSha256) +
                  ", apkDigestSha256='" + apkDigestSha256 + '\'' +
                  ", ctsProfileMatch=" + ctsProfileMatch +
                  ", basicIntegrity=" + basicIntegrity +
                  '}';
      }
  }
  </code></pre>
  
  <p>We can easily call above workable code by calling this line of code inside activity:</p>
  
  <pre><code>    new SafetyNetVerifier(this);
  </code></pre>
  
  <p>The result is:</p>
  
  <pre><code>SafetyNetResponse{
   nonce='Xc4dSnAjAqf9KWDZokwK2TdBw9Td+ZILU2FmZXR5IE5ldCBTYW1wbGU6IDE0ODcxODQyMjYwNjc=', 
  timestampMs=1487184225994, 
  apkPackageName='null', 
  apkCertificateDigestSha256=[], 
  apkDigestSha256='null', 
  ctsProfileMatch=false, 
  basicIntegrity=false
  }
  </code></pre>
  
  <p>As timestamp parsed correctly. I think I have get successfully safetynet response. But I don't know why <code>apkPackageName</code> always null and other fields as I have shown are empty. Please help me.</p>