1 /*
2  * Copyright (C) 2023 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 android.net.wifi.util;
18 
19 import android.annotation.Nullable;
20 import android.os.PersistableBundle;
21 
22 import java.util.Arrays;
23 import java.util.Objects;
24 
25 /**
26  * Utilities for {@link PersistableBundle}, which does not provide its own implementations of
27  * equals() and hashCode().
28  *
29  * @hide
30  */
31 public class PersistableBundleUtils {
32     /** Generate a hashcode for the provided PersistableBundle. */
getHashCode(@ullable PersistableBundle bundle)33     public static int getHashCode(@Nullable PersistableBundle bundle) {
34         if (bundle == null) {
35             return -1;
36         }
37 
38         int iterativeHashcode = 0;
39         for (String key : bundle.keySet()) {
40             Object val = bundle.get(key);
41             if (val instanceof PersistableBundle) {
42                 iterativeHashcode =
43                         Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
44             } else if (val.getClass().isArray()) {
45                 iterativeHashcode = Objects.hash(iterativeHashcode, key, getArrayHashCode(val));
46             } else {
47                 iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
48             }
49         }
50 
51         return iterativeHashcode;
52     }
53 
getArrayHashCode(Object arr)54     private static int getArrayHashCode(Object arr) {
55         if (arr instanceof boolean[]) {
56             return Arrays.hashCode((boolean[]) arr);
57         } else if (arr instanceof double[]) {
58             return Arrays.hashCode((double[]) arr);
59         } else if (arr instanceof int[]) {
60             return Arrays.hashCode((int[]) arr);
61         } else if (arr instanceof long[]) {
62             return Arrays.hashCode((long[]) arr);
63         } else if (arr instanceof String[]) {
64             return Arrays.hashCode((String[]) arr);
65         }
66         return -1;
67     }
68 
arraysEqual(Object left, Object right)69     private static boolean arraysEqual(Object left, Object right) {
70         if (left instanceof boolean[]) {
71             return Arrays.equals((boolean[]) left, (boolean[]) right);
72         } else if (left instanceof double[]) {
73             return Arrays.equals((double[]) left, (double[]) right);
74         } else if (left instanceof int[]) {
75             return Arrays.equals((int[]) left, (int[]) right);
76         } else if (left instanceof long[]) {
77             return Arrays.equals((long[]) left, (long[]) right);
78         } else if (left instanceof String[]) {
79             return Arrays.equals((String[]) left, (String[]) right);
80         }
81         return false;
82     }
83 
84     /** Check whether the provided PersistableBundles are equal. */
isEqual( @ullable PersistableBundle left, @Nullable PersistableBundle right)85     public static boolean isEqual(
86             @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
87         // Check for pointer equality and null equality.
88         if (Objects.equals(left, right)) {
89             return true;
90         }
91         if (Objects.isNull(left) != Objects.isNull(right)) {
92             return false;
93         }
94         if (!left.keySet().equals(right.keySet())) {
95             return false;
96         }
97 
98         for (String key : left.keySet()) {
99             Object leftVal = left.get(key);
100             Object rightVal = right.get(key);
101 
102             if (Objects.equals(leftVal, rightVal)) {
103                 continue;
104             } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
105                 // If classes are different, not equal by definition.
106                 return false;
107             }
108 
109             if (leftVal instanceof PersistableBundle) {
110                 if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
111                     return false;
112                 }
113             } else if (leftVal.getClass().isArray()) {
114                 if (!arraysEqual(leftVal, rightVal)) {
115                     return false;
116                 }
117             } else {
118                 if (!Objects.equals(leftVal, rightVal)) {
119                     return false;
120                 }
121             }
122         }
123         return true;
124     }
125 }
126